From 86454c6245f43efc9054980f7891286c6ca6b9c6 Mon Sep 17 00:00:00 2001 From: laiiihz Date: Thu, 16 Nov 2023 20:59:34 +0800 Subject: [PATCH] update tools --- lib/models/app_atom.dart | 31 +--- lib/routers/app_router.dart | 32 +--- lib/routers/app_router.g.dart | 14 +- .../uri_parser/urI_parser.provider.dart | 16 ++ .../uri_parser/urI_parser.provider.g.dart | 24 +++ .../uri_parser/uri_parser.dart | 116 +++++++++++++ .../uri_parser/uri_parser_provider.dart | 47 ----- .../uri_parser/uri_parser_view.dart | 65 ------- lib/tools/formatters/dart/dart_format.dart | 58 ------- .../formatters/dart/dart_format.provider.dart | 52 ------ .../dart/dart_format.provider.g.dart | 56 ------ lib/tools/formatters/dart/dart_formatter.dart | 119 +++++++++++++ lib/tools/formatters/json/json_format.dart | 82 --------- .../formatters/json/json_format.provider.dart | 90 ---------- .../json/json_format.provider.g.dart | 71 -------- lib/tools/formatters/json/json_formatter.dart | 157 +++++++++++++++++ lib/tools/info/device_info/device_info.dart | 24 +++ .../info/network_info/network_info_view.dart | 53 +++--- .../date_parser/date_format_helper.dart | 90 ++++++++++ .../date_parser/date_operation_widget.dart | 82 --------- .../date_parser/date_parsed_widget.dart | 42 ----- .../text_tools/date_parser/date_parser.dart | 162 +++++++++++++++++- .../date_parser/date_parser.provider.dart | 19 +- .../date_parser/date_parser.provider.g.dart | 17 +- .../date_parser/date_parser_view.dart | 118 ------------- .../date_parser_view_provider.dart | 94 ---------- .../date_parser_view_provider.g.dart | 85 --------- lib/tools/tools.dart | 26 +++ lib/ui/widgets/app_text_field.dart | 3 + lib/ui/widgets/asset_svg.dart | 1 - .../configurations/configurations.dart | 2 +- lib/ui/widgets/error_message_expandable.dart | 51 ++---- .../widgets/scaffold/scrollable_scaffold.dart | 23 ++- lib/ui/widgets/tool_view.dart | 1 + .../lib/custom_highlights/uri_highlight.dart | 2 +- pubspec.lock | 87 +++++----- pubspec.yaml | 5 +- 37 files changed, 903 insertions(+), 1114 deletions(-) create mode 100644 lib/tools/encoders_decoders/uri_parser/urI_parser.provider.dart create mode 100644 lib/tools/encoders_decoders/uri_parser/urI_parser.provider.g.dart create mode 100644 lib/tools/encoders_decoders/uri_parser/uri_parser.dart delete mode 100644 lib/tools/encoders_decoders/uri_parser/uri_parser_provider.dart delete mode 100644 lib/tools/encoders_decoders/uri_parser/uri_parser_view.dart delete mode 100644 lib/tools/formatters/dart/dart_format.dart delete mode 100644 lib/tools/formatters/dart/dart_format.provider.dart delete mode 100644 lib/tools/formatters/dart/dart_format.provider.g.dart create mode 100644 lib/tools/formatters/dart/dart_formatter.dart delete mode 100644 lib/tools/formatters/json/json_format.dart delete mode 100644 lib/tools/formatters/json/json_format.provider.dart delete mode 100644 lib/tools/formatters/json/json_format.provider.g.dart create mode 100644 lib/tools/formatters/json/json_formatter.dart create mode 100644 lib/tools/text_tools/date_parser/date_format_helper.dart delete mode 100644 lib/tools/text_tools/date_parser/date_operation_widget.dart delete mode 100644 lib/tools/text_tools/date_parser/date_parsed_widget.dart delete mode 100644 lib/tools/text_tools/date_parser/date_parser_view.dart delete mode 100644 lib/tools/text_tools/date_parser/date_parser_view_provider.dart delete mode 100644 lib/tools/text_tools/date_parser/date_parser_view_provider.g.dart create mode 100644 lib/tools/tools.dart diff --git a/lib/models/app_atom.dart b/lib/models/app_atom.dart index c28572c..768876c 100644 --- a/lib/models/app_atom.dart +++ b/lib/models/app_atom.dart @@ -1,32 +1,7 @@ // ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:alga/models/app_category.dart'; import 'package:alga/routers/app_router.dart'; -import 'package:alga/tools/converters/abs_length_converter/abs_length_converter_view.dart'; -import 'package:alga/tools/converters/color_converter/color_converter_view.dart'; -import 'package:alga/tools/converters/json_yaml_converter/json_yaml_converter_view.dart'; -import 'package:alga/tools/converters/number_base_converter/number_base_converter_view.dart'; -import 'package:alga/tools/encoders_decoders/base_64_encoder_decoder/base_64_encoder_decoder.dart'; -import 'package:alga/tools/encoders_decoders/gzip_compress_decompress/gzip_compress_decompress_view.dart'; -import 'package:alga/tools/encoders_decoders/jwt_decoder/jwt_decoder_view.dart'; -import 'package:alga/tools/encoders_decoders/uri_encoder_decoder/uri_encoder_decoder.dart'; -import 'package:alga/tools/encoders_decoders/uri_parser/uri_parser_view.dart'; -import 'package:alga/tools/formatters/dart/dart_format.dart'; -import 'package:alga/tools/formatters/json/json_format.dart'; -import 'package:alga/tools/generators/hash_generator/hash_gen.dart'; -import 'package:alga/tools/generators/lorem_ipsum_generator/lorem_ipsum_gen.dart'; -import 'package:alga/tools/generators/password_generator/password_gen.dart'; -import 'package:alga/tools/generators/random_file_generator/random_file_generator_view.dart'; -import 'package:alga/tools/generators/sass_css_generator/sass2css.dart'; -import 'package:alga/tools/generators/uuid_generator/uuid_gen.dart'; -import 'package:alga/tools/image_tools/blur_hash_tool/blur_hash.dart'; -import 'package:alga/tools/image_tools/qrcode_tool/qrcode.dart'; -import 'package:alga/tools/info/device_info/device_info.dart'; -import 'package:alga/tools/info/network_info/network_info_view.dart'; -import 'package:alga/tools/js_tools/quick_js_tool/quick_js_view.dart'; -import 'package:alga/tools/server_tools/static_server_tool/static_server_tool_view.dart'; -import 'package:alga/tools/text_tools/date_parser/date_parser_view.dart'; -import 'package:alga/tools/text_tools/markdown_preview/markdown_preview_view.dart'; -import 'package:alga/tools/text_tools/regex_tester/regex_tester.dart'; +import 'package:alga/tools/tools.dart'; import 'package:alga/ui/widgets/svg_asset_icon.dart'; import 'package:alga/utils/constants/import_helper.dart'; @@ -273,13 +248,13 @@ class AppAtom { static final jsonFormatter = AppAtom( icon: const SvgAssetIcon('assets/icons/json.svg', colorIcon: true), title: (context) => context.tr.formatterJson, - path: JsonFormatRoute().location, + path: JsonFormatterRoute().location, categories: [AppCategory.formatter], ); static final dartFormatter = AppAtom( icon: const SvgAssetIcon('assets/icons/dart.svg', colorIcon: true), title: (context) => context.tr.formatterDart, - path: DartFormatRoute().location, + path: DartFormatterRoute().location, categories: [AppCategory.formatter], ); diff --git a/lib/routers/app_router.dart b/lib/routers/app_router.dart index fcb1243..68d1648 100644 --- a/lib/routers/app_router.dart +++ b/lib/routers/app_router.dart @@ -1,28 +1,4 @@ -import 'package:alga/tools/converters/abs_length_converter/abs_length_converter_view.dart'; -import 'package:alga/tools/converters/color_converter/color_converter_view.dart'; -import 'package:alga/tools/converters/json_yaml_converter/json_yaml_converter_view.dart'; -import 'package:alga/tools/converters/number_base_converter/number_base_converter_view.dart'; -import 'package:alga/tools/encoders_decoders/base_64_encoder_decoder/base_64_encoder_decoder.dart'; -import 'package:alga/tools/encoders_decoders/gzip_compress_decompress/gzip_compress_decompress_view.dart'; -import 'package:alga/tools/encoders_decoders/jwt_decoder/jwt_decoder_view.dart'; -import 'package:alga/tools/encoders_decoders/uri_encoder_decoder/uri_encoder_decoder.dart'; -import 'package:alga/tools/encoders_decoders/uri_parser/uri_parser_view.dart'; -import 'package:alga/tools/formatters/dart/dart_format.dart'; -import 'package:alga/tools/formatters/json/json_format.dart'; -import 'package:alga/tools/generators/hash_generator/hash_gen.dart'; -import 'package:alga/tools/generators/lorem_ipsum_generator/lorem_ipsum_gen.dart'; -import 'package:alga/tools/generators/password_generator/password_gen.dart'; -import 'package:alga/tools/generators/random_file_generator/random_file_generator_view.dart'; -import 'package:alga/tools/generators/sass_css_generator/sass2css.dart'; -import 'package:alga/tools/generators/uuid_generator/uuid_gen.dart'; -import 'package:alga/tools/image_tools/blur_hash_tool/blur_hash.dart'; -import 'package:alga/tools/image_tools/qrcode_tool/qrcode.dart'; -import 'package:alga/tools/info/device_info/device_info.dart'; -import 'package:alga/tools/info/network_info/network_info_view.dart'; -import 'package:alga/tools/js_tools/quick_js_tool/quick_js_view.dart'; -import 'package:alga/tools/server_tools/static_server_tool/static_server_tool_view.dart'; -import 'package:alga/tools/text_tools/date_parser/date_parser_view.dart'; -import 'package:alga/tools/text_tools/markdown_preview/markdown_preview_view.dart'; +import 'package:alga/tools/tools.dart'; import 'package:alga/ui/alga_view/alga_view.dart'; import 'package:alga/ui/alga_view/all_apps/alga_app_view.dart'; import 'package:alga/ui/views/favorite_view.dart'; @@ -32,8 +8,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; -import '../tools/text_tools/regex_tester/regex_tester.dart'; - part 'app_router.g.dart'; final GlobalKey _routerKey = @@ -74,8 +48,8 @@ GoRouter appRouter(AppRouterRef ref) { path: 'gzip-compress-decompress'), TypedGoRoute(path: 'jwt-decoder'), TypedGoRoute(path: 'uri-parser'), - TypedGoRoute(path: 'json-format'), - TypedGoRoute(path: 'dart-format'), + TypedGoRoute(path: 'json-format'), + TypedGoRoute(path: 'dart-format'), TypedGoRoute(path: 'static-server-tool'), TypedGoRoute(path: 'device-info'), TypedGoRoute(path: 'network-info'), diff --git a/lib/routers/app_router.g.dart b/lib/routers/app_router.g.dart index 7fbc971..14aec6c 100644 --- a/lib/routers/app_router.g.dart +++ b/lib/routers/app_router.g.dart @@ -99,11 +99,11 @@ RouteBase get $rootRoute => ShellRouteData.$route( ), GoRouteData.$route( path: 'json-format', - factory: $JsonFormatRouteExtension._fromState, + factory: $JsonFormatterRouteExtension._fromState, ), GoRouteData.$route( path: 'dart-format', - factory: $DartFormatRouteExtension._fromState, + factory: $DartFormatterRouteExtension._fromState, ), GoRouteData.$route( path: 'static-server-tool', @@ -515,8 +515,9 @@ extension $UriParserRouteExtension on UriParserRoute { void replace(BuildContext context) => context.replace(location); } -extension $JsonFormatRouteExtension on JsonFormatRoute { - static JsonFormatRoute _fromState(GoRouterState state) => JsonFormatRoute(); +extension $JsonFormatterRouteExtension on JsonFormatterRoute { + static JsonFormatterRoute _fromState(GoRouterState state) => + JsonFormatterRoute(); String get location => GoRouteData.$location( '/apps/json-format', @@ -532,8 +533,9 @@ extension $JsonFormatRouteExtension on JsonFormatRoute { void replace(BuildContext context) => context.replace(location); } -extension $DartFormatRouteExtension on DartFormatRoute { - static DartFormatRoute _fromState(GoRouterState state) => DartFormatRoute(); +extension $DartFormatterRouteExtension on DartFormatterRoute { + static DartFormatterRoute _fromState(GoRouterState state) => + DartFormatterRoute(); String get location => GoRouteData.$location( '/apps/dart-format', diff --git a/lib/tools/encoders_decoders/uri_parser/urI_parser.provider.dart b/lib/tools/encoders_decoders/uri_parser/urI_parser.provider.dart new file mode 100644 index 0000000..64e7110 --- /dev/null +++ b/lib/tools/encoders_decoders/uri_parser/urI_parser.provider.dart @@ -0,0 +1,16 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import 'uri_parser.dart'; + +part 'urI_parser.provider.g.dart'; + +@riverpod +(Uri?, String?) uri(UriRef ref) { + final content = ref.watch(useContent); + if (content.isEmpty) return (null, null); + try { + return (Uri.parse(content), null); + } on FormatException catch (e) { + return (null, e.message); + } +} diff --git a/lib/tools/encoders_decoders/uri_parser/urI_parser.provider.g.dart b/lib/tools/encoders_decoders/uri_parser/urI_parser.provider.g.dart new file mode 100644 index 0000000..0bcaa82 --- /dev/null +++ b/lib/tools/encoders_decoders/uri_parser/urI_parser.provider.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'urI_parser.provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$uriHash() => r'16eabca2649f4fc56f5454dba4a81688a74f75cb'; + +/// See also [uri]. +@ProviderFor(uri) +final uriProvider = AutoDisposeProvider<(Uri?, String?)>.internal( + uri, + name: r'uriProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$uriHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef UriRef = AutoDisposeProviderRef<(Uri?, String?)>; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/tools/encoders_decoders/uri_parser/uri_parser.dart b/lib/tools/encoders_decoders/uri_parser/uri_parser.dart new file mode 100644 index 0000000..7a38101 --- /dev/null +++ b/lib/tools/encoders_decoders/uri_parser/uri_parser.dart @@ -0,0 +1,116 @@ +import 'dart:convert'; + +import 'package:alga/l10n/l10n.dart'; +import 'package:alga/tools/encoders_decoders/uri_parser/urI_parser.provider.dart'; +import 'package:alga/tools/tools.provider.dart'; +import 'package:alga/ui/widgets/app_text_field.dart'; +import 'package:alga/ui/widgets/buttons/clear_button.dart'; +import 'package:alga/ui/widgets/buttons/copy_button.dart'; +import 'package:alga/ui/widgets/configurations/configurations.dart'; +import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart'; +import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart'; +import 'package:alga/ui/widgets/toolbar/toolbar_paste.dart'; +import 'package:alga/utils/extension/list_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:language_textfield/language_textfield.dart'; + +final useContent = stringConfigProvider(const Key('content')); + +class UriParserRoute extends GoRouteData { + @override + Widget build(BuildContext context, GoRouterState state) { + return const UriParserPage(); + } +} + +class UriParserPage extends ConsumerStatefulWidget { + const UriParserPage({super.key}); + + @override + ConsumerState createState() => _UriParserPageState(); +} + +class _UriParserPageState extends ConsumerState { + // TODO(laiiihz): uri highlight + final _controller = TextEditingController(); + @override + void initState() { + super.initState(); + _controller.addListener(() { + ref.read(useContent.notifier).change(_controller.text); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final result = ref.watch(uriProvider); + final currentUri = result.$1 ?? Uri(); + return ScrollableScaffold( + title: Text(context.tr.uriParser), + children: [ + AlgaToolbar( + title: Text(context.tr.input), + actions: [ + ToolbarPaste(controller: _controller), + ClearButton(controller: _controller), + ], + ), + TextField( + controller: _controller, + decoration: InputDecoration(errorText: result.$2), + ), + CrossFade( + state: result.$1 != null, + first: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + ...{ + if (currentUri.hasScheme) + context.tr.uriScheme: currentUri.scheme, + context.tr.hostName: currentUri.host, + if (currentUri.hasPort) + context.tr.uriPort: currentUri.port.toString(), + context.tr.uriPath: currentUri.path, + if (currentUri.hasScheme) + context.tr.uriOrigin: currentUri.origin, + if (currentUri.hasAuthority) + 'authority': currentUri.authority, + }.entries.map((e) { + return AppTextField( + text: e.value, + decoration: InputDecoration( + labelText: e.key, + suffixIcon: CopyButton(() => e.value), + ), + ); + }), + if (currentUri.hasQuery) + Builder(builder: (context) { + final text = const JsonEncoder.withIndent(' ') + .convert(currentUri.queryParameters); + return AppTextField( + language: HighlightType.json, + text: text, + decoration: InputDecoration( + labelText: context.tr.uriParams, + suffixIcon: CopyButton(() => text), + ), + ); + }), + ].sep(const SizedBox(height: 8)), + ), + ), + ), + ], + ); + } +} diff --git a/lib/tools/encoders_decoders/uri_parser/uri_parser_provider.dart b/lib/tools/encoders_decoders/uri_parser/uri_parser_provider.dart deleted file mode 100644 index 58166d2..0000000 --- a/lib/tools/encoders_decoders/uri_parser/uri_parser_provider.dart +++ /dev/null @@ -1,47 +0,0 @@ -part of './uri_parser_view.dart'; - -final _input = Provider.autoDispose((ref) { - final controller = TextEditingController(); - ref.onDispose(controller.dispose); - return controller; -}); - -final _uri = - Provider.autoDispose((ref) => Uri.tryParse(ref.watch(_input).text)); - -final _uriParts = Provider.autoDispose>((ref) { - final uri = ref.watch(_uri); - if (uri == null) return []; - String? origin; - try { - origin = uri.origin; - } catch (e) { - origin = null; - } - - String params = JsonEncoder.withIndent(' ' * 4).convert(uri.queryParameters); - return [ - UriPart(title: (context) => S.of(context).uriHost, name: uri.host), - if (origin != null) - UriPart(title: (context) => S.of(context).uriOrigin, name: origin), - UriPart(title: (context) => S.of(context).uriScheme, name: uri.scheme), - UriPart(title: (context) => S.of(context).uriPort, name: '${uri.port}'), - UriPart(title: (context) => S.of(context).uriPath, name: uri.path), - UriPart( - title: (context) => S.of(context).uriParams, - name: params, - lang: LangHighlightType.json, - ), - ]; -}); - -class UriPart { - final String Function(BuildContext context) title; - final String name; - final String? lang; - UriPart({ - required this.title, - required this.name, - this.lang, - }); -} diff --git a/lib/tools/encoders_decoders/uri_parser/uri_parser_view.dart b/lib/tools/encoders_decoders/uri_parser/uri_parser_view.dart deleted file mode 100644 index b4ed14e..0000000 --- a/lib/tools/encoders_decoders/uri_parser/uri_parser_view.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'dart:convert'; - -import 'package:alga/utils/constants/import_helper.dart'; -import 'package:alga/ui/widgets/copy_button_widget.dart'; -import 'package:alga/ui/widgets/paste_button_widget.dart'; - -part './uri_parser_provider.dart'; - -class UriParserRoute extends GoRouteData { - @override - Widget build(BuildContext context, GoRouterState state) { - return const UriParserView(); - } -} - -class UriParserView extends StatefulWidget { - const UriParserView({super.key}); - - @override - State createState() => _UriParserViewState(); -} - -class _UriParserViewState extends State { - @override - Widget build(BuildContext context) { - return ScrollableToolView( - title: Text(S.of(context).uriParser), - children: [ - AppTitleWrapper( - title: S.of(context).input, - actions: [ - PasteButtonWidget( - _input, - onUpdate: (ref) => ref.refresh(_uri), - ), - ], - child: Consumer(builder: (context, ref, _) { - return LangTextField( - lang: LangHighlightType.uri, - controller: ref.watch(_input), - onChanged: (_) => ref.refresh(_uri), - ); - }), - ), - Consumer(builder: (context, ref, _) { - return Column( - children: ref - .watch(_uriParts) - .map((e) => AppTitleWrapper( - title: e.title(context), - actions: [ - CopyButtonWithText.raw(e.name), - ], - child: AppTextField( - text: e.name, - lang: e.lang, - ), - )) - .toList(), - ); - }), - ], - ); - } -} diff --git a/lib/tools/formatters/dart/dart_format.dart b/lib/tools/formatters/dart/dart_format.dart deleted file mode 100644 index f347988..0000000 --- a/lib/tools/formatters/dart/dart_format.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:alga/tools/formatters/dart/dart_format.provider.dart'; -import 'package:alga/ui/widgets/error_message_expandable.dart'; -import 'package:alga/ui/widgets/scaffold/tool_actions.dart'; -import 'package:alga/ui/widgets/scaffold/tool_copy.dart'; -import 'package:alga/ui/widgets/scaffold/tool_scaffold.dart'; -import 'package:alga/utils/constants/import_helper.dart'; - -class DartFormatRoute extends GoRouteData { - @override - Widget build(BuildContext context, GoRouterState state) { - return const DartFormatView(); - } -} - -class DartFormatView extends ConsumerStatefulWidget { - const DartFormatView({super.key}); - - @override - ConsumerState createState() => _DartFormatViewState(); -} - -class _DartFormatViewState extends ConsumerState { - @override - Widget build(BuildContext context) { - return ToolScaffold( - title: Text(context.tr.formatterDart), - body: ToolActions( - childExpanded: true, - actions: [ - if (ref.watch(dartFormatLoadingProvider)) - const CircularProgressIndicator.adaptive(), - const Spacer(), - ToolCopy.icon(ref.watch(dartContentProvider)), - FilledButton.tonalIcon( - onPressed: () { - ref.read(dartContentProvider.notifier).format(); - }, - icon: const Icon(Icons.format_indent_decrease_rounded), - label: Text(context.tr.format), - ), - ], - child: TextField( - expands: true, - minLines: null, - maxLines: null, - onChanged: (value) { - ref.read(errorMessageProvider.notifier).clear(); - }, - textAlignVertical: TextAlignVertical.top, - controller: ref.watch(dartContentProvider), - decoration: InputDecoration( - error: ErrorMessageWidget.get(ref.watch(errorMessageProvider)), - ), - ), - ), - ); - } -} diff --git a/lib/tools/formatters/dart/dart_format.provider.dart b/lib/tools/formatters/dart/dart_format.provider.dart deleted file mode 100644 index 283fd6a..0000000 --- a/lib/tools/formatters/dart/dart_format.provider.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:alga/utils/constants/import_helper.dart'; -import 'package:dart_style/dart_style.dart'; -import 'package:flutter/foundation.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'dart_format.provider.g.dart'; - -@riverpod -class DartFormatLoading extends _$DartFormatLoading { - @override - bool build() => false; - - update(bool value) => state = value; -} - -@riverpod -class ErrorMessage extends _$ErrorMessage { - @override - String? build() => null; - void update(String? value) => state = value; - void clear() => state = null; -} - -@riverpod -class DartContent extends _$DartContent { - @override - Raw build() { - final controller = RichTextController.lang(type: HighlightType.dart); - ref.onDispose(controller.dispose); - return controller; - } - - FutureOr format() async { - try { - final text = state.text; - if (text.length > 2000) { - state.text = _format(text); - } else { - ref.read(dartFormatLoadingProvider.notifier).update(true); - state.text = await compute(_format, text); - } - } catch (e) { - ref.read(errorMessageProvider.notifier).update(e.toString()); - } finally { - ref.read(dartFormatLoadingProvider.notifier).update(false); - } - } -} - -String _format(String text) { - return DartFormatter().format(text); -} diff --git a/lib/tools/formatters/dart/dart_format.provider.g.dart b/lib/tools/formatters/dart/dart_format.provider.g.dart deleted file mode 100644 index af30d2b..0000000 --- a/lib/tools/formatters/dart/dart_format.provider.g.dart +++ /dev/null @@ -1,56 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'dart_format.provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$dartFormatLoadingHash() => r'65879101302a646d39b0fe1fb7542e1a3c255213'; - -/// See also [DartFormatLoading]. -@ProviderFor(DartFormatLoading) -final dartFormatLoadingProvider = - AutoDisposeNotifierProvider.internal( - DartFormatLoading.new, - name: r'dartFormatLoadingProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$dartFormatLoadingHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$DartFormatLoading = AutoDisposeNotifier; -String _$errorMessageHash() => r'0f52963ddb6e9e754bfb32bc4be309f07d4234d9'; - -/// See also [ErrorMessage]. -@ProviderFor(ErrorMessage) -final errorMessageProvider = - AutoDisposeNotifierProvider.internal( - ErrorMessage.new, - name: r'errorMessageProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$errorMessageHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ErrorMessage = AutoDisposeNotifier; -String _$dartContentHash() => r'7cac4bd0f187ee1319d9f848c1badafa701ea252'; - -/// See also [DartContent]. -@ProviderFor(DartContent) -final dartContentProvider = - AutoDisposeNotifierProvider>.internal( - DartContent.new, - name: r'dartContentProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$dartContentHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$DartContent = AutoDisposeNotifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/tools/formatters/dart/dart_formatter.dart b/lib/tools/formatters/dart/dart_formatter.dart new file mode 100644 index 0000000..1876d6f --- /dev/null +++ b/lib/tools/formatters/dart/dart_formatter.dart @@ -0,0 +1,119 @@ +import 'package:alga/l10n/l10n.dart'; +import 'package:alga/ui/widgets/buttons/copy_button.dart'; +import 'package:alga/ui/widgets/configurations/configurations.dart'; +import 'package:alga/ui/widgets/custom_icon_button.dart'; +import 'package:alga/ui/widgets/error_message_expandable.dart'; +import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart'; +import 'package:alga/ui/widgets/toolbar/toolbar_paste.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:language_textfield/language_textfield.dart'; + +class DartFormatterRoute extends GoRouteData { + @override + Widget build(BuildContext context, GoRouterState state) { + return const DartFormatterPage(); + } +} + +class DartFormatterPage extends ConsumerStatefulWidget { + const DartFormatterPage({super.key}); + + @override + ConsumerState createState() => + _DartFormatterPageState(); +} + +class _DartFormatterPageState extends ConsumerState { + final RichTextController controller = + RichTextController.lang(type: HighlightType.dart); + final _loading = ValueNotifier(false); + final _errorMessage = ValueNotifier(null); + + @override + void dispose() { + controller.dispose(); + _loading.dispose(); + _errorMessage.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(context.tr.formatterDart), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + AlgaToolbar( + title: ValueListenableBuilder( + valueListenable: _loading, + builder: (context, state, child) { + return CrossFade( + state: !state, + first: const Text('Dart'), + second: const CircularProgressIndicator.adaptive(), + ); + }, + ), + actions: [ + CopyButton(() => controller.text), + ToolbarPaste(controller: controller), + CustomIconButton( + tooltip: context.tr.format, + onPressed: format, + icon: const Icon(Icons.format_indent_decrease_rounded), + ), + ], + ), + Expanded( + child: ValueListenableBuilder( + valueListenable: _errorMessage, + builder: (context, message, _) { + return TextField( + expands: true, + maxLines: null, + minLines: null, + textAlignVertical: TextAlignVertical.top, + controller: controller, + decoration: InputDecoration( + error: ErrorMessageWidget.get(message), + ), + ); + }), + ), + ], + ), + ), + ); + } + + Future format() async { + final text = controller.text; + if (text.length > 2000) { + _loading.value = true; + final result = await compute(_format, text); + controller.text = result.$1; + _errorMessage.value = result.$2; + _loading.value = false; + } else { + final result = _format(text); + controller.text = result.$1; + _errorMessage.value = result.$2; + } + } +} + +(String, String?) _format(String text) { + try { + return (DartFormatter().format(text), null); + } catch (e) { + return (text, e.toString()); + } +} diff --git a/lib/tools/formatters/json/json_format.dart b/lib/tools/formatters/json/json_format.dart deleted file mode 100644 index d8c8750..0000000 --- a/lib/tools/formatters/json/json_format.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:alga/tools/formatters/json/json_format.provider.dart'; -import 'package:alga/ui/widgets/error_message_expandable.dart'; -import 'package:alga/ui/widgets/scaffold/tool_actions.dart'; -import 'package:alga/ui/widgets/scaffold/tool_options.dart'; -import 'package:alga/ui/widgets/scaffold/tool_scaffold.dart'; -import 'package:alga/utils/constants/import_helper.dart'; - -class JsonFormatRoute extends GoRouteData { - @override - Widget build(BuildContext context, GoRouterState state) { - return const JsonFormatView(); - } -} - -class JsonFormatView extends ConsumerStatefulWidget { - const JsonFormatView({super.key}); - - @override - ConsumerState createState() => _JsonFormatViewState(); -} - -class _JsonFormatViewState extends ConsumerState { - @override - Widget build(BuildContext context) { - return ToolScaffold( - title: Text(context.tr.formatterJson), - options: [ - ToolOptionsEnum( - provider: jsonIndentProvider, - title: Text(context.tr.indentation), - leading: const Icon(Icons.format_indent_decrease), - items: JsonIndentType.values, - l10n: (context, value) => value.name(context), - onSelect: (value) => - ref.read(jsonIndentProvider.notifier).change(value), - ), - ], - body: ToolActions( - childExpanded: true, - actions: [ - if (ref.watch(jsonFormatLoadingProvider)) - const CircularProgressIndicator.adaptive(), - const Spacer(), - TextButton.icon( - onPressed: () { - ref.read(jsonContentProvider.notifier).format(); - }, - icon: const Icon(Icons.format_align_left_sharp), - label: const Text('Format'), - ), - ], - child: TextField( - expands: true, - minLines: null, - maxLines: null, - buildCounter: ( - context, { - required int currentLength, - required bool isFocused, - int? maxLength, - }) { - final bf = StringBuffer(); - final controller = ref.read(jsonContentProvider); - final select = - (controller.selection.start - controller.selection.end).abs(); - if (select != 0) { - bf.write(select); - bf.write('/'); - } - bf.write(currentLength); - return Text(bf.toString()); - }, - decoration: InputDecoration( - error: ErrorMessageWidget.get(ref.watch(errorMessageProvider)), - ), - controller: ref.watch(jsonContentProvider), - textAlignVertical: TextAlignVertical.top, - ), - ), - ); - } -} diff --git a/lib/tools/formatters/json/json_format.provider.dart b/lib/tools/formatters/json/json_format.provider.dart deleted file mode 100644 index 2044e5c..0000000 --- a/lib/tools/formatters/json/json_format.provider.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'dart:convert'; - -import 'package:alga/utils/constants/import_helper.dart'; -import 'package:flutter/foundation.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'json_format.provider.g.dart'; - -enum JsonIndentType { - space2(' '), - space4(' '), - tab('\t'), - minified(null), - ; - - const JsonIndentType(this.indent); - - final String? indent; - - String name(BuildContext context) { - return switch (this) { - JsonIndentType.space2 => S.of(context).json2spaces, - JsonIndentType.space4 => S.of(context).json4spaces, - JsonIndentType.tab => S.of(context).json1tab, - JsonIndentType.minified => S.of(context).jsonMinified, - }; - } -} - -@Riverpod(keepAlive: true) -class JsonIndent extends _$JsonIndent { - @override - JsonIndentType build() => JsonIndentType.space2; - - change(JsonIndentType type) { - state = type; - } -} - -@riverpod -class JsonContent extends _$JsonContent { - @override - Raw build() { - final controller = RichTextController.lang(type: HighlightType.json); - ref.onDispose(controller.dispose); - controller.addListener(() { - ref.read(errorMessageProvider.notifier).clear(); - }); - return controller; - } - - FutureOr format() async { - try { - final indent = ref.read(jsonIndentProvider); - if (state.text.length < 1500) { - state.text = _format((state.text, indent)); - return; - } else { - ref.read(jsonFormatLoadingProvider.notifier).update(true); - state.text = await compute(_format, (state.text, indent)); - } - } on FormatException catch (e) { - ref.read(errorMessageProvider.notifier).update(e.message); - } finally { - ref.read(jsonFormatLoadingProvider.notifier).update(false); - } - } -} - -@riverpod -class JsonFormatLoading extends _$JsonFormatLoading { - @override - bool build() => false; - - void update(bool value) => state = value; -} - -@riverpod -class ErrorMessage extends _$ErrorMessage { - @override - String? build() => null; - - update(String? text) => state = text; - clear() => state = null; -} - -String _format((String data, JsonIndentType indent) params) { - final jsonMap = const JsonDecoder().convert(params.$1); - return JsonEncoder.withIndent(params.$2.indent).convert(jsonMap); -} diff --git a/lib/tools/formatters/json/json_format.provider.g.dart b/lib/tools/formatters/json/json_format.provider.g.dart deleted file mode 100644 index 04905e6..0000000 --- a/lib/tools/formatters/json/json_format.provider.g.dart +++ /dev/null @@ -1,71 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'json_format.provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$jsonIndentHash() => r'0ecbdc98b481d3af97259fb1136964f4d3032128'; - -/// See also [JsonIndent]. -@ProviderFor(JsonIndent) -final jsonIndentProvider = - NotifierProvider.internal( - JsonIndent.new, - name: r'jsonIndentProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jsonIndentHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$JsonIndent = Notifier; -String _$jsonContentHash() => r'e16c178e258524cde0997beebf855756c7229a59'; - -/// See also [JsonContent]. -@ProviderFor(JsonContent) -final jsonContentProvider = - AutoDisposeNotifierProvider>.internal( - JsonContent.new, - name: r'jsonContentProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$jsonContentHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$JsonContent = AutoDisposeNotifier>; -String _$jsonFormatLoadingHash() => r'83f5ca6966c768be50b9652b96180f5fddee039a'; - -/// See also [JsonFormatLoading]. -@ProviderFor(JsonFormatLoading) -final jsonFormatLoadingProvider = - AutoDisposeNotifierProvider.internal( - JsonFormatLoading.new, - name: r'jsonFormatLoadingProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$jsonFormatLoadingHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$JsonFormatLoading = AutoDisposeNotifier; -String _$errorMessageHash() => r'96d341807f56317a206de2a92405ce08d17645ed'; - -/// See also [ErrorMessage]. -@ProviderFor(ErrorMessage) -final errorMessageProvider = - AutoDisposeNotifierProvider.internal( - ErrorMessage.new, - name: r'errorMessageProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$errorMessageHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$ErrorMessage = AutoDisposeNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/tools/formatters/json/json_formatter.dart b/lib/tools/formatters/json/json_formatter.dart new file mode 100644 index 0000000..6afcc94 --- /dev/null +++ b/lib/tools/formatters/json/json_formatter.dart @@ -0,0 +1,157 @@ +import 'dart:convert'; + +import 'package:alga/l10n/l10n.dart'; +import 'package:alga/ui/widgets/buttons/copy_button.dart'; +import 'package:alga/ui/widgets/configurations/configurations.dart'; +import 'package:alga/ui/widgets/custom_icon_button.dart'; +import 'package:alga/ui/widgets/error_message_expandable.dart'; +import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart'; +import 'package:alga/ui/widgets/toolbar/toolbar_paste.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:language_textfield/language_textfield.dart'; + +class JsonFormatterRoute extends GoRouteData { + @override + Widget build(BuildContext context, GoRouterState state) { + return const JsonFormatterPage(); + } +} + +class JsonFormatterPage extends ConsumerStatefulWidget { + const JsonFormatterPage({super.key}); + + @override + ConsumerState createState() => + _JsonFormatterPageState(); +} + +class _JsonFormatterPageState extends ConsumerState { + final RichTextController controller = + RichTextController.lang(type: HighlightType.json); + final _loading = ValueNotifier(false); + final _errorMessage = ValueNotifier(null); + JsonIndentType _type = JsonIndentType.space2; + + @override + void dispose() { + controller.dispose(); + _loading.dispose(); + _errorMessage.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(context.tr.formatterJson), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + ConfigMenu( + items: JsonIndentType.values, + getName: (t) => t.name(context), + title: Text(context.tr.indentation), + initItem: _type, + onSelect: (t) { + _type = t; + format(); + setState(() {}); + }, + ), + AlgaToolbar( + title: ValueListenableBuilder( + valueListenable: _loading, + builder: (context, state, child) { + return CrossFade( + state: !state, + first: Text(context.tr.formatterJson), + second: const CircularProgressIndicator.adaptive(), + ); + }, + ), + actions: [ + CopyButton(() => controller.text), + ToolbarPaste(controller: controller), + CustomIconButton( + tooltip: context.tr.format, + onPressed: format, + icon: const Icon(Icons.format_indent_decrease_rounded), + ), + ], + ), + Expanded( + child: ValueListenableBuilder( + valueListenable: _errorMessage, + builder: (context, message, _) { + return TextField( + expands: true, + maxLines: null, + minLines: null, + textAlignVertical: TextAlignVertical.top, + controller: controller, + decoration: InputDecoration( + error: ErrorMessageWidget.get(message), + ), + ); + }), + ), + ], + ), + ), + ); + } + + Future format() async { + final text = controller.text; + if (text.length > 2000) { + _loading.value = true; + final result = await compute(_format, (text: text, type: _type)); + controller.text = result.$1; + _errorMessage.value = result.$2; + _loading.value = false; + } else { + final result = _format((text: text, type: _type)); + controller.text = result.$1; + _errorMessage.value = result.$2; + } + } +} + +enum JsonIndentType { + space2(' '), + space4(' '), + tab('\t'), + minified(null), + ; + + const JsonIndentType(this.indent); + + final String? indent; + + String name(BuildContext context) { + return switch (this) { + space2 => S.of(context).json2spaces, + space4 => S.of(context).json4spaces, + tab => S.of(context).json1tab, + minified => S.of(context).jsonMinified, + }; + } +} + +(String, String?) _format(({String text, JsonIndentType type}) input) { + try { + final result = JsonEncoder.withIndent(input.type.indent) + .convert(json.decode(input.text)); + return (result, null); + } on JsonUnsupportedObjectError catch (e) { + return (input.text, e.cause.toString()); + } catch (e) { + return (input.text, e.toString()); + } +} diff --git a/lib/tools/info/device_info/device_info.dart b/lib/tools/info/device_info/device_info.dart index dceb685..30f3c6c 100644 --- a/lib/tools/info/device_info/device_info.dart +++ b/lib/tools/info/device_info/device_info.dart @@ -3,7 +3,9 @@ import 'dart:io'; import 'package:alga/tools/info/device_info/android_logo.dart'; import 'package:alga/tools/info/device_info/device_info.provider.dart'; import 'package:alga/tools/info/widgets/device_tile.dart'; +import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart'; import 'package:alga/ui/widgets/scaffold/tool_scaffold.dart'; +import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart'; import 'package:alga/utils/constants/import_helper.dart'; class DeviceInfoRoute extends GoRouteData { @@ -18,6 +20,28 @@ class DeviceInfoView extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + return ScrollableScaffold( + title: Text(context.tr.deviceInfo), + actions: [ + if (Platform.isAndroid) const AndroidLogo(), + ], + children: [ + AlgaToolbar(title: Text(context.tr.commonInfo)), + ...Part.fromCommon().map((p) { + return DeviceTile(title: p.title(context), value: p.value); + }), + AlgaToolbar(title: Text(context.tr.platformInfo)), + ...ref.watch(deviceInfoPartsProvider).whenOrNull( + data: (data) => data.map((e) { + return DeviceTile( + title: e.title(context), value: e.value); + })) ?? + [], + ], + ); + } + + Widget build2(BuildContext context, WidgetRef ref) { return ToolScaffold.scroll( title: Text(context.tr.deviceInfo), actions: [ diff --git a/lib/tools/info/network_info/network_info_view.dart b/lib/tools/info/network_info/network_info_view.dart index f7235b8..6a1a184 100644 --- a/lib/tools/info/network_info/network_info_view.dart +++ b/lib/tools/info/network_info/network_info_view.dart @@ -1,6 +1,9 @@ +import 'package:alga/l10n/l10n.dart'; import 'package:alga/tools/info/network_info/network_info.provider.dart'; -import 'package:alga/ui/widgets/scaffold/tool_scaffold.dart'; -import 'package:alga/utils/constants/import_helper.dart'; +import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import '../widgets/device_tile.dart'; @@ -16,32 +19,26 @@ class NetworkInfoView extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - return ToolScaffold.scroll( - title: Text(S.of(context).networkInfo), - body: Column( - children: [ - ref.watch(networkInfoWrapperProvider).when( - data: (data) { - return Column( - children: data - .content(context) - .map( - (e) => DeviceTile(title: e.$2, value: e.$1)) - .toList(), - ); - }, - error: (e, s) => Text(e.toString()), - loading: () => - const Center(child: CircularProgressIndicator.adaptive()), - ), - ref.watch(connectivityProvider).whenOrNull( - data: (data) { - return DeviceTile(title: 'mode', value: data.name); - }, - ) ?? - const SizedBox.shrink(), - ], - ), + return ScrollableScaffold( + title: Text(context.tr.networkInfo), + children: [ + ...ref.watch(networkInfoWrapperProvider).when( + data: (data) { + return data + .content(context) + .map((e) => DeviceTile(title: e.$2, value: e.$1)); + }, + error: (e, s) => [Text(e.toString())], + loading: () => + const [Center(child: CircularProgressIndicator.adaptive())], + ), + ref.watch(connectivityProvider).whenOrNull( + data: (data) { + return DeviceTile(title: 'mode', value: data.name); + }, + ) ?? + const SizedBox.shrink(), + ], ); } } diff --git a/lib/tools/text_tools/date_parser/date_format_helper.dart b/lib/tools/text_tools/date_parser/date_format_helper.dart new file mode 100644 index 0000000..c965745 --- /dev/null +++ b/lib/tools/text_tools/date_parser/date_format_helper.dart @@ -0,0 +1,90 @@ +import 'package:alga/l10n/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:two_dimensional_scrollables/two_dimensional_scrollables.dart'; + +class DateFormatHelper extends StatelessWidget { + const DateFormatHelper({super.key}); + + static show(BuildContext context) { + showDialog( + context: context, + useRootNavigator: false, + builder: (context) => AlertDialog( + title: const Text('Foramt Table'), + content: const DateFormatHelper(), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text(context.mtr.okButtonLabel), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 512, + width: 600, + child: TableView.builder( + columnCount: 4, + rowCount: FormatData.values.length, + pinnedColumnCount: 1, + pinnedRowCount: 1, + columnBuilder: (index) { + const lengths = [64, 240, 128, 128]; + return TableSpan(extent: FixedTableSpanExtent(lengths[index])); + }, + rowBuilder: (index) { + return const TableSpan(extent: FixedTableSpanExtent(32)); + }, + cellBuilder: (context, v) { + final item = FormatData.values[v.row]; + switch (v.column) { + case 0: + return Text(item.symbol); + case 1: + return Text(item.meaning); + case 2: + return Text(item.presentation); + case 3: + return Text(item.example); + } + return const SizedBox.shrink(); + }, + ), + ); + } +} + +class FormatData { + final String symbol; + final String meaning; + final String presentation; + final String example; + const FormatData(this.symbol, this.meaning, this.presentation, this.example); + + static const values = [ + FormatData('Symbol', 'Meaning', 'Presentation', 'Example'), + FormatData('G', 'era designator', 'Text', 'AD'), + FormatData('y', 'year', 'Number', '1996'), + FormatData('M', 'month in year', 'Text & Number', 'July & 07'), + FormatData('L', 'standalone month', 'Text & Number', 'July & 07'), + FormatData('d', 'day in month', 'Number', '10'), + FormatData('c', 'standalone day', 'Number', '10'), + FormatData('h', 'hour in am/pm (1~12)', 'Number', '12'), + FormatData('H', 'hour in day (0~23)', 'Number', '0'), + FormatData('m', 'minute in hour', 'Number', '30'), + FormatData('s', 'second in minute', 'Number', '55'), + FormatData('S', 'fractional second', 'Number', '978'), + FormatData('E', 'day of week', 'Text', 'Tuesday'), + FormatData('D', 'day in year', 'Number', '189'), + FormatData('a', 'am/pm marker', 'Text', 'PM'), + FormatData('k', 'hour in day (1~24)', 'Number', '24'), + FormatData('K', 'hour in am/pm (0~11)', 'Number', '0'), + FormatData('Q', 'quarter', 'Text', 'Q3'), + FormatData("'", 'escape for text', 'Delimiter', "'Date='"), + FormatData("''", 'single quote', 'Literal', "'o''clock'") + ]; +} diff --git a/lib/tools/text_tools/date_parser/date_operation_widget.dart b/lib/tools/text_tools/date_parser/date_operation_widget.dart deleted file mode 100644 index 10c6010..0000000 --- a/lib/tools/text_tools/date_parser/date_operation_widget.dart +++ /dev/null @@ -1,82 +0,0 @@ -part of './date_parser_view.dart'; - -class DateOperationWidget extends ConsumerWidget { - const DateOperationWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final date = ref.watch(dateParserProvider); - if (date == null) return const SizedBox.shrink(); - return Wrap( - spacing: 4, - runSpacing: 4, - children: [ - DateOprt( - text: context.tr.nextDay, - onChange: (date) => date.nextDay, - ), - DateOprt( - text: context.tr.previousDay, - onChange: (date) => date.previousDay, - ), - DateOprt( - text: context.tr.nextMonth, - onChange: (date) => date.nextMonth, - ), - DateOprt( - text: context.tr.previousMonth, - onChange: (date) => date.previousMonth, - ), - DateOprt( - text: context.tr.nextYear, - onChange: (date) => date.nextYear, - ), - DateOprt( - text: context.tr.previousYear, - onChange: (date) => date.previousYear, - ), - DateOprt( - text: context.tr.nextHour, - onChange: (date) => date.nextHour, - ), - DateOprt( - text: context.tr.previousHour, - onChange: (date) => date.previousHour, - ), - DateOprt( - text: context.tr.nextMinute, - onChange: (date) => date.nextMinute, - ), - DateOprt( - text: context.tr.previousMinute, - onChange: (date) => date.previousMinute, - ), - DateOprt( - text: context.tr.nextSecond, - onChange: (date) => date.nextSecond, - ), - DateOprt( - text: context.tr.previousSecond, - onChange: (date) => date.previousSecond, - ), - ], - ); - } -} - -class DateOprt extends ConsumerWidget { - final String text; - final DateTime Function(DateTime current) onChange; - const DateOprt({super.key, required this.text, required this.onChange}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return OutlinedButton( - onPressed: () { - final date = onChange(ref.read(dateParserProvider)!); - ref.read(dateParserProvider.notifier).update(date); - }, - child: Text(text), - ); - } -} diff --git a/lib/tools/text_tools/date_parser/date_parsed_widget.dart b/lib/tools/text_tools/date_parser/date_parsed_widget.dart deleted file mode 100644 index c1ea6fe..0000000 --- a/lib/tools/text_tools/date_parser/date_parsed_widget.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:alga/utils/constants/import_helper.dart'; - -class DateParsedWidget extends StatelessWidget { - final DateTime date; - const DateParsedWidget({super.key, required this.date}); - - @override - Widget build(BuildContext context) { - GestureDetector coloredChip(String data, [String? helpText]) => - GestureDetector( - onTap: () { - ClipboardUtil.copy(data); - }, - child: Tooltip( - message: S.of(context).clickToCopy, - child: Chip( - label: Text('$data${helpText == null ? '' : ' ($helpText) '}'), - ), - ), - ); - final tzOffset = date.timeZoneOffset.inHours; - final weekdays = MaterialLocalizations.of(context).narrowWeekdays; - String weekday = date.weekday == 7 ? weekdays[0] : weekdays[date.weekday]; - return Wrap( - spacing: 4, - runSpacing: 4, - children: [ - coloredChip('${date.year}'.padLeft(4, '0'), 'Year'), - coloredChip('${date.month}'.padLeft(2, '0'), 'Month'), - coloredChip('${date.day}'.padLeft(2, '0'), 'Day'), - coloredChip('${date.hour}'.padLeft(2, '0'), 'Hour'), - coloredChip('${date.minute}'.padLeft(2, '0'), 'Minute'), - coloredChip('${date.second}'.padLeft(2, '0'), 'Second'), - coloredChip('${date.millisecond}'.padLeft(3, '0'), 'Millisecond'), - coloredChip(weekday, 'Weekday'), - if (date.isUtc) coloredChip('UTC'), - coloredChip(date.timeZoneName), - coloredChip(tzOffset.isNegative ? '$tzOffset' : '+$tzOffset'), - ], - ); - } -} diff --git a/lib/tools/text_tools/date_parser/date_parser.dart b/lib/tools/text_tools/date_parser/date_parser.dart index ec23bed..8283f2d 100644 --- a/lib/tools/text_tools/date_parser/date_parser.dart +++ b/lib/tools/text_tools/date_parser/date_parser.dart @@ -1,11 +1,19 @@ import 'package:alga/l10n/l10n.dart'; +import 'package:alga/tools/text_tools/date_parser/date_ext.dart'; +import 'package:alga/tools/text_tools/date_parser/date_format_helper.dart'; import 'package:alga/tools/tools.provider.dart'; +import 'package:alga/ui/widgets/app_text_field.dart'; +import 'package:alga/ui/widgets/buttons/copy_button.dart'; import 'package:alga/ui/widgets/buttons/help_button.dart'; import 'package:alga/ui/widgets/configurations/configurations.dart'; +import 'package:alga/ui/widgets/custom_icon_button.dart'; import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart'; import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart'; +import 'package:alga/utils/clipboard_util.dart'; +import 'package:alga/utils/extension/list_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import 'date_parser.provider.dart'; @@ -13,6 +21,14 @@ final useCustomFormat = booleanConfigProvider(const Key('use_custom_format')); final useCustomFormatContent = stringConfigProvider(const Key('custom_format')); final useContent = stringConfigProvider(const Key('use_content')); +final useFormatContent = stringConfigProvider(const Key('format_content')); + +class DateParserRoute extends GoRouteData { + @override + Widget build(BuildContext context, GoRouterState state) { + return const DateParserPage(); + } +} class DateParserPage extends ConsumerStatefulWidget { const DateParserPage({super.key}); @@ -23,22 +39,29 @@ class DateParserPage extends ConsumerStatefulWidget { class _DateParserPageState extends ConsumerState { final _input = TextEditingController(); + final _formatInput = TextEditingController(); @override void initState() { super.initState(); _input.addListener(() { ref.read(useContent.notifier).change(_input.text); }); + _formatInput.addListener(() { + ref.read(useFormatContent.notifier).change(_formatInput.text); + }); } @override void dispose() { _input.dispose(); + _formatInput.dispose(); super.dispose(); } @override Widget build(BuildContext context) { + final dateResult = ref.watch(dateResultProvider); + final parsedDate = dateResult.$1 ?? DateTime.now(); return ScrollableScaffold( title: Text(context.tr.dateParser), configurations: [ @@ -59,13 +82,80 @@ class _DateParserPageState extends ConsumerState { AlgaToolbar( title: Text(context.tr.dateParserdate), actions: [ + TextButton.icon( + onPressed: () { + DateFormatHelper.show(context); + }, + icon: const Icon(Icons.help), + label: const Text('FORMAT'), + ), + CustomIconButton( + tooltip: context.tr.currentDate, + onPressed: () { + _input.text = DateTime.now().toIso8601String(); + }, + icon: const Icon(Icons.access_time_rounded), + ), HelpButton( onPressed: showSupportedDateFormatDialog, tooltip: context.tr.dateFormatHelp, ), ], ), - TextField(controller: _input), + TextField( + controller: _input, + decoration: InputDecoration(errorText: dateResult.$2), + ), + const AlgaToolbar(), + AppTextField(text: dateResult.$1?.toIso8601String()), + CrossFade( + state: dateResult.$1 != null, + first: Column( + children: [ + CanCopyDate(dateResult.$1 ?? DateTime.now()), + const AlgaToolbar(title: Text('Date Operations')), + Wrap( + spacing: 8, + runSpacing: 8, + children: { + context.tr.previousYear: () => parsedDate.previousYear, + context.tr.nextYear: () => parsedDate.nextYear, + context.tr.previousMonth: () => parsedDate.previousMonth, + context.tr.nextMonth: () => parsedDate.nextMonth, + context.tr.previousDay: () => parsedDate.previousDay, + context.tr.nextDay: () => parsedDate.nextDay, + context.tr.previousHour: () => parsedDate.previousHour, + context.tr.nextHour: () => parsedDate.nextHour, + context.tr.previousMinute: () => parsedDate.previousMinute, + context.tr.nextMinute: () => parsedDate.nextMinute, + context.tr.previousSecond: () => parsedDate.previousSecond, + context.tr.nextSecond: () => parsedDate.nextSecond, + } + .entries + .map((e) => OperationChip( + name: e.key, + onTap: () { + _input.text = e.value().toIso8601String(); + }, + )) + .toList(), + ), + AlgaToolbar( + title: Text(context.tr.format), + actions: [ + HelpButton(onPressed: () => DateFormatHelper.show(context)), + ], + ), + TextField(controller: _formatInput), + AlgaToolbar( + actions: [ + CopyButton(() => ref.read(formatDateResultProvider)), + ], + ), + AppTextField(text: ref.watch(formatDateResultProvider)), + ].sep(const SizedBox(height: 4)), + ), + ), ], ); } @@ -94,3 +184,73 @@ class _DateParserPageState extends ConsumerState { ); } } + +class CanCopyDate extends StatelessWidget { + const CanCopyDate(this.date, {super.key}); + + final DateTime date; + + @override + Widget build(BuildContext context) { + return Material( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: { + 'timestamp': date.millisecondsSinceEpoch, + 'Year': date.year, + 'Month': date.month, + 'Day': date.day, + 'Hour': date.hour, + 'Minute': date.minute, + 'Second': date.second, + 'Millisecond': date.millisecond, + 'Microsecond': date.microsecond, + 'Timezone': date.timeZoneName, + 'Timezone offset': + (date.timeZoneOffset.inMinutes / 60).toStringAsFixed(1), + 'isUTC': date.isUtc, + 'weekday': context + .mtr.narrowWeekdays[date.weekday == 7 ? 0 : date.weekday], + }.entries.map((e) => CopyChip(e.key, e.value)).toList(), + ), + ), + ); + } +} + +class CopyChip extends StatelessWidget { + const CopyChip(this.name, this.label, {super.key}); + final String name; + final Object label; + @override + Widget build(BuildContext context) { + return ActionChip.elevated( + label: Text.rich(TextSpan( + children: [ + TextSpan( + text: name, + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + const TextSpan(text: ' '), + TextSpan(text: label.toString()), + ], + )), + onPressed: () { + ClipboardUtil.copy(label.toString()); + }, + ); + } +} + +class OperationChip extends StatelessWidget { + const OperationChip({super.key, required this.name, required this.onTap}); + final String name; + final VoidCallback onTap; + @override + Widget build(BuildContext context) { + return ActionChip.elevated(label: Text(name), onPressed: onTap); + } +} diff --git a/lib/tools/text_tools/date_parser/date_parser.provider.dart b/lib/tools/text_tools/date_parser/date_parser.provider.dart index acce268..ccf3872 100644 --- a/lib/tools/text_tools/date_parser/date_parser.provider.dart +++ b/lib/tools/text_tools/date_parser/date_parser.provider.dart @@ -1,3 +1,4 @@ +import 'package:intl/intl.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'date_parser.dart'; @@ -30,8 +31,13 @@ const List supportedDateFormat = [ if (customFormat) { final customFormatContent = ref.watch(useCustomFormatContent); - //TODO - return (null, null); + try { + return (DateFormat(customFormatContent).parse(content), null); + } on FormatException catch (e) { + return (null, e.message); + } catch (e) { + return (null, e.toString()); + } } else { try { return (DateTime.parse(content), null); @@ -42,3 +48,12 @@ const List supportedDateFormat = [ } } } + +@riverpod +String formatDateResult(FormatDateResultRef ref) { + final format = ref.watch(useFormatContent); + final date = ref.watch(dateResultProvider); + if (date.$1 == null) return ''; + if (format.isEmpty) return ''; + return DateFormat(format).format(date.$1!); +} diff --git a/lib/tools/text_tools/date_parser/date_parser.provider.g.dart b/lib/tools/text_tools/date_parser/date_parser.provider.g.dart index 5f54b97..18baeec 100644 --- a/lib/tools/text_tools/date_parser/date_parser.provider.g.dart +++ b/lib/tools/text_tools/date_parser/date_parser.provider.g.dart @@ -6,7 +6,7 @@ part of 'date_parser.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$dateResultHash() => r'aebb3888b062669ec21343f6093a3a0dfc89f11a'; +String _$dateResultHash() => r'c8d718f6de9d5b23b284b6e19923a3272429e014'; /// See also [dateResult]. @ProviderFor(dateResult) @@ -20,5 +20,20 @@ final dateResultProvider = AutoDisposeProvider<(DateTime?, String?)>.internal( ); typedef DateResultRef = AutoDisposeProviderRef<(DateTime?, String?)>; +String _$formatDateResultHash() => r'8600b5dfd891e27efb78d1dcf4402a43a9f95b0b'; + +/// See also [formatDateResult]. +@ProviderFor(formatDateResult) +final formatDateResultProvider = AutoDisposeProvider.internal( + formatDateResult, + name: r'formatDateResultProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$formatDateResultHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef FormatDateResultRef = AutoDisposeProviderRef; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/tools/text_tools/date_parser/date_parser_view.dart b/lib/tools/text_tools/date_parser/date_parser_view.dart deleted file mode 100644 index 83ce7d5..0000000 --- a/lib/tools/text_tools/date_parser/date_parser_view.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:alga/tools/text_tools/date_parser/date_parser.dart'; -import 'package:alga/ui/widgets/copy_button_widget.dart'; -import 'package:alga/ui/widgets/paste_button_widget.dart'; -import 'package:alga/utils/constants/import_helper.dart'; -import 'package:alga/tools/text_tools/date_parser/date_ext.dart'; -import 'package:alga/tools/text_tools/date_parser/date_parsed_widget.dart'; - -import 'date_parser_view_provider.dart'; - -part './date_operation_widget.dart'; - -class DateParserRoute extends GoRouteData { - @override - Widget build(BuildContext context, GoRouterState state) { - return const DateParserView(); - } -} - -class DateParserView extends StatefulWidget { - const DateParserView({super.key}); - - @override - State createState() => _DateParserViewState(); -} - -class _DateParserViewState extends State { - @override - Widget build(BuildContext context) { - return DateParserPage(); - return ScrollableToolView( - title: Text(S.of(context).dateParser), - children: [ - AppTitleWrapper( - title: S.of(context).dateParserdate, - actions: [ - HelperIconButton( - title: Text(context.tr.supportedDateFormat), - content: Wrap( - spacing: 4, - runSpacing: 4, - children: [ - for (var item in supportDateFormat) Chip(label: Text(item)) - ], - ), - ), - Consumer(builder: (context, ref, _) { - return TextButton( - onPressed: () { - ref.read(dateControllerProvider).text = - DateTime.now().toIso8601String(); - }, - child: Text(context.tr.currentDate), - ); - }), - ], - child: Consumer(builder: (context, ref, _) { - return TextField( - controller: ref.read(dateControllerProvider), - ); - }), - ), - Consumer(builder: (context, ref, _) { - final date = ref.watch(dateParserProvider); - if (date == null) return const SizedBox.shrink(); - return Column( - children: [ - AppTitleWrapper( - title: S.of(context).parsedDate, - child: DateParsedWidget(date: date), - ), - AppTitleWrapper( - title: S.of(context).iso8601Date, - actions: [ - CopyButtonWidget(text: date.toIso8601String()), - ], - child: AppTextField(text: date.toIso8601String()), - ), - ], - ); - }), - AppTitleWrapper( - title: context.tr.dateCalculation, - child: const DateOperationWidget(), - ), - Consumer(builder: (context, ref, _) { - return AppTitleWrapper( - title: context.tr.dateCustomFormat, - actions: [ - HelperIconButton( - title: Text(context.tr.dateFormatHelp), - content: Wrap( - spacing: 4, - runSpacing: 4, - children: [ - for (var item in dateFormatHelp) Chip(label: Text(item)) - ], - ), - ), - PasteButtonWidget(formatControllerProvider), - ], - child: TextField( - controller: ref.read(formatControllerProvider), - ), - ); - }), - Consumer(builder: (context, ref, _) { - return AppTitleWrapper( - title: S.of(context).formattedDateString, - actions: [ - CopyButtonWithText.raw(ref.watch(formatResultProvider)), - ], - child: AppTextField(text: ref.watch(formatResultProvider) ?? ''), - ); - }), - ], - ); - } -} diff --git a/lib/tools/text_tools/date_parser/date_parser_view_provider.dart b/lib/tools/text_tools/date_parser/date_parser_view_provider.dart deleted file mode 100644 index a689ddb..0000000 --- a/lib/tools/text_tools/date_parser/date_parser_view_provider.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:alga/utils/constants/import_helper.dart'; -import 'package:intl/intl.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'date_parser_view_provider.g.dart'; - -@riverpod -TextEditingController dateController(DateControllerRef ref) { - final controller = TextEditingController(); - controller.addListener(() => ref.invalidate(dateParserProvider)); - ref.onDispose(controller.dispose); - return controller; -} - -@riverpod -class DateParser extends _$DateParser { - @override - DateTime? build() { - final date = ref.watch(dateControllerProvider).text; - final parsedDate = DateTime.tryParse(date); - if (parsedDate != null) { - return parsedDate; - } - int? timestamp = int.tryParse(date); - if (timestamp != null && timestamp >= 0) { - return DateTime.fromMillisecondsSinceEpoch(timestamp); - } - return null; - } - - update(DateTime date) { - state = date; - } -} - -@riverpod -TextEditingController formatController(FormatControllerRef ref) { - final controller = TextEditingController(); - ref.onDispose(controller.dispose); - controller.addListener(() => ref.invalidate(foramtTextProvider)); - return controller; -} - -@riverpod -String foramtText(ForamtTextRef ref) { - return ref.watch(formatControllerProvider).text; -} - -@riverpod -String? formatResult(FormatResultRef ref) { - final date = ref.watch(dateParserProvider); - final format = ref.watch(foramtTextProvider); - if (date == null) return null; - return DateFormat(format).format(date); -} - -const List supportDateFormat = [ - "2012-02-27", - "2012-02-27 13:27:00", - "2012-02-27 13:27:00.123456789z", - "2012-02-27 13:27:00,123456789z", - "20120227 13:27:00", - "20120227T132700", - "20120227", - "+20120227", - "2012-02-27T14Z", - "2012-02-27T14+00:00", - "-123450101 00:00:00 Z", - "2002-02-27T14:00:00-0500", - "2002-02-27T19:00:00Z", - "timestamp", -]; - -List dateFormatHelp = [ - 'G: era designator', - 'y: year', - 'M: month in year', - 'L: standalone month', - 'd: day in month', - 'c: standalone day', - 'h: hour in am/pm (1~12)', - 'H: hour in day (0~23)', - 'm: minute in hour', - 's: second in minute', - 'S: fractional second', - 'E: day of week', - 'D: day in year', - 'a: am/pm marker', - 'k: hour in day (1~24)', - 'K: hour in am/pm (0~11)', - 'Q: quarter', - '\': escape for text', - '\'\': single quote', -]; diff --git a/lib/tools/text_tools/date_parser/date_parser_view_provider.g.dart b/lib/tools/text_tools/date_parser/date_parser_view_provider.g.dart deleted file mode 100644 index 79b6b80..0000000 --- a/lib/tools/text_tools/date_parser/date_parser_view_provider.g.dart +++ /dev/null @@ -1,85 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'date_parser_view_provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$dateControllerHash() => r'925b244b30dda6a909d9e169f7bd370f41b55ef9'; - -/// See also [dateController]. -@ProviderFor(dateController) -final dateControllerProvider = - AutoDisposeProvider.internal( - dateController, - name: r'dateControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$dateControllerHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef DateControllerRef = AutoDisposeProviderRef; -String _$formatControllerHash() => r'49eb6f83c105022efea1aaab12a86e4a36e86b3e'; - -/// See also [formatController]. -@ProviderFor(formatController) -final formatControllerProvider = - AutoDisposeProvider.internal( - formatController, - name: r'formatControllerProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$formatControllerHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef FormatControllerRef = AutoDisposeProviderRef; -String _$foramtTextHash() => r'6b0200bf99ca9b49bc9b30dcce66c2a4037ab106'; - -/// See also [foramtText]. -@ProviderFor(foramtText) -final foramtTextProvider = AutoDisposeProvider.internal( - foramtText, - name: r'foramtTextProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$foramtTextHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef ForamtTextRef = AutoDisposeProviderRef; -String _$formatResultHash() => r'aaa7ca66aa985e1aa0da41869c73dd6fde1b2df4'; - -/// See also [formatResult]. -@ProviderFor(formatResult) -final formatResultProvider = AutoDisposeProvider.internal( - formatResult, - name: r'formatResultProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$formatResultHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef FormatResultRef = AutoDisposeProviderRef; -String _$dateParserHash() => r'fb2bf6c3d29608aba97e4080d34c1598989f33ef'; - -/// See also [DateParser]. -@ProviderFor(DateParser) -final dateParserProvider = - AutoDisposeNotifierProvider.internal( - DateParser.new, - name: r'dateParserProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$dateParserHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$DateParser = AutoDisposeNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/tools/tools.dart b/lib/tools/tools.dart new file mode 100644 index 0000000..6f44a6b --- /dev/null +++ b/lib/tools/tools.dart @@ -0,0 +1,26 @@ +export 'converters/abs_length_converter/abs_length_converter_view.dart'; +export 'converters/color_converter/color_converter_view.dart'; +export 'converters/json_yaml_converter/json_yaml_converter_view.dart'; +export 'converters/number_base_converter/number_base_converter_view.dart'; +export 'encoders_decoders/base_64_encoder_decoder/base_64_encoder_decoder.dart'; +export 'encoders_decoders/gzip_compress_decompress/gzip_compress_decompress_view.dart'; +export 'encoders_decoders/jwt_decoder/jwt_decoder_view.dart'; +export 'encoders_decoders/uri_encoder_decoder/uri_encoder_decoder.dart'; +export 'encoders_decoders/uri_parser/uri_parser.dart'; +export 'formatters/dart/dart_formatter.dart'; +export 'formatters/json/json_formatter.dart'; +export 'generators/hash_generator/hash_gen.dart' hide useContent; +export 'generators/lorem_ipsum_generator/lorem_ipsum_gen.dart'; +export 'generators/password_generator/password_gen.dart' hide useCount; +export 'generators/random_file_generator/random_file_generator_view.dart'; +export 'generators/sass_css_generator/sass2css.dart'; +export 'generators/uuid_generator/uuid_gen.dart'; +export 'image_tools/blur_hash_tool/blur_hash.dart'; +export 'image_tools/qrcode_tool/qrcode.dart'; +export 'info/device_info/device_info.dart'; +export 'info/network_info/network_info_view.dart'; +export 'js_tools/quick_js_tool/quick_js_view.dart'; +export 'server_tools/static_server_tool/static_server_tool_view.dart'; +export 'text_tools/date_parser/date_parser.dart' hide useContent; +export 'text_tools/markdown_preview/markdown_preview_view.dart'; +export 'text_tools/regex_tester/regex_tester.dart'; diff --git a/lib/ui/widgets/app_text_field.dart b/lib/ui/widgets/app_text_field.dart index 6a0c650..5c0a929 100644 --- a/lib/ui/widgets/app_text_field.dart +++ b/lib/ui/widgets/app_text_field.dart @@ -10,6 +10,7 @@ class AppTextField extends StatefulWidget { this.minLines, this.maxLines, this.expands = false, + this.decoration = const InputDecoration(), }); final String? text; @@ -19,6 +20,7 @@ class AppTextField extends StatefulWidget { final int? minLines; final int? maxLines; final bool expands; + final InputDecoration? decoration; @override State createState() => _AppTextFieldState(); @@ -59,6 +61,7 @@ class _AppTextFieldState extends State { expands: widget.expands, textAlign: TextAlign.start, textAlignVertical: TextAlignVertical.top, + decoration: widget.decoration, ); } } diff --git a/lib/ui/widgets/asset_svg.dart b/lib/ui/widgets/asset_svg.dart index a698c57..0b71288 100644 --- a/lib/ui/widgets/asset_svg.dart +++ b/lib/ui/widgets/asset_svg.dart @@ -25,4 +25,3 @@ class AssetSvg extends StatelessWidget { } // currentcolor - diff --git a/lib/ui/widgets/configurations/configurations.dart b/lib/ui/widgets/configurations/configurations.dart index 06e6934..ce6f1ee 100644 --- a/lib/ui/widgets/configurations/configurations.dart +++ b/lib/ui/widgets/configurations/configurations.dart @@ -164,6 +164,7 @@ class _ConfigTextFieldState extends ConsumerState { _focusNode.addListener(() { if (!_focusNode.hasFocus) { widget.onEditingComplete?.call(ref); + ref.read(widget.provider.notifier).change(_controller.text); } }); } @@ -196,7 +197,6 @@ class _ConfigTextFieldState extends ConsumerState { if (!_form.currentState!.validate()) { return; } - ref.read(widget.provider.notifier).change(_controller.text); _focusNode.unfocus(); }, ); diff --git a/lib/ui/widgets/error_message_expandable.dart b/lib/ui/widgets/error_message_expandable.dart index 2ece9c8..a0af339 100644 --- a/lib/ui/widgets/error_message_expandable.dart +++ b/lib/ui/widgets/error_message_expandable.dart @@ -1,5 +1,4 @@ import 'package:alga/utils/constants/import_helper.dart'; -import 'package:flutter/cupertino.dart'; class ErrorMessageWidget extends StatelessWidget { const ErrorMessageWidget(this.message, {super.key}); @@ -12,16 +11,27 @@ class ErrorMessageWidget extends StatelessWidget { final String message; @override Widget build(BuildContext context) { - return InkResponse( - onTap: () { - showAdaptiveDialog( + final colorScheme = Theme.of(context).colorScheme; + return TextButton.icon( + style: TextButton.styleFrom( + foregroundColor: colorScheme.error, + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + minimumSize: Size.zero, + ), + onPressed: () { + showDialog( context: context, builder: (context) { - return AlertDialog.adaptive( + return AlertDialog( + scrollable: true, + backgroundColor: colorScheme.errorContainer.withOpacity(0.5), title: const Text('Error'), - content: Text(message), + content: Text( + message * 3, + style: TextStyle(color: colorScheme.onErrorContainer), + ), actions: [ - AdaptiveDialogAction( + TextButton( onPressed: () => Navigator.of(context).pop(), child: Text(context.mtr.okButtonLabel), ), @@ -30,9 +40,8 @@ class ErrorMessageWidget extends StatelessWidget { }, ); }, - highlightShape: BoxShape.rectangle, - radius: 32, - child: Text( + icon: const Icon(Icons.error_rounded), + label: Text( message, maxLines: 1, softWrap: false, @@ -41,25 +50,3 @@ class ErrorMessageWidget extends StatelessWidget { ); } } - -class AdaptiveDialogAction extends StatelessWidget { - const AdaptiveDialogAction( - {super.key, required this.onPressed, required this.child}); - final VoidCallback onPressed; - final Widget child; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - switch (theme.platform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - return TextButton(onPressed: onPressed, child: child); - case TargetPlatform.iOS: - case TargetPlatform.macOS: - return CupertinoDialogAction(onPressed: onPressed, child: child); - } - } -} diff --git a/lib/ui/widgets/scaffold/scrollable_scaffold.dart b/lib/ui/widgets/scaffold/scrollable_scaffold.dart index 27ea2bd..807ee7a 100644 --- a/lib/ui/widgets/scaffold/scrollable_scaffold.dart +++ b/lib/ui/widgets/scaffold/scrollable_scaffold.dart @@ -2,28 +2,46 @@ import 'package:alga/utils/constants/import_helper.dart'; import 'package:alga/utils/extension/list_ext.dart'; +/// +/// --- AppBar --- +/// +/// --- configurations --- +/// +/// --- children --- +/// +/// --- child --- +/// +/// --- slivers --- +/// +/// --- fillRemain --- class ScrollableScaffold extends StatelessWidget { const ScrollableScaffold({ super.key, required this.title, this.configurations = const [], this.children = const [], + this.actions = const [], + this.slivers = const [], this.fillRemain, + this.hasScrollBody = false, this.child, }); final Widget title; final List configurations; final List children; + final List actions; + final List slivers; final Widget? child; final Widget? fillRemain; + final bool hasScrollBody; @override Widget build(BuildContext context) { return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar.medium(title: title), + SliverAppBar.medium(title: title, actions: actions), if (configurations.isNotEmpty) SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), @@ -43,11 +61,12 @@ class ScrollableScaffold extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), sliver: SliverToBoxAdapter(child: child), ), + ...slivers, if (fillRemain != null) SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), sliver: SliverFillRemaining( - hasScrollBody: false, + hasScrollBody: hasScrollBody, child: fillRemain, ), ), diff --git a/lib/ui/widgets/tool_view.dart b/lib/ui/widgets/tool_view.dart index d792ae8..8cdcbc6 100644 --- a/lib/ui/widgets/tool_view.dart +++ b/lib/ui/widgets/tool_view.dart @@ -28,6 +28,7 @@ class ToolView extends StatelessWidget { } } +@Deprecated('use ScrollableScaffold') class ScrollableToolView extends StatelessWidget { const ScrollableToolView({ super.key, diff --git a/packages/highlight_textspan/lib/custom_highlights/uri_highlight.dart b/packages/highlight_textspan/lib/custom_highlights/uri_highlight.dart index 49a7f5c..a35712a 100644 --- a/packages/highlight_textspan/lib/custom_highlights/uri_highlight.dart +++ b/packages/highlight_textspan/lib/custom_highlights/uri_highlight.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; List uriHighlight(String data) { - //TODO uriHighlight + //TODO(laiiihz): uriHighlight throw UnimplementedError(); } diff --git a/pubspec.lock b/pubspec.lock index 07a2eea..c9f1bb8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.flutter-io.cn" source: hosted - version: "61.0.0" + version: "64.0.0" adaptive_breakpoints: dependency: "direct main" description: @@ -21,18 +21,18 @@ packages: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.flutter-io.cn" source: hosted - version: "5.13.0" + version: "6.2.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" url: "https://pub.flutter-io.cn" source: hosted - version: "0.11.2" + version: "0.11.3" animations: dependency: "direct main" description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.flutter-io.cn" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -269,10 +269,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: ac86d3abab0f165e4b8f561280ff4e066bceaac83c424dd19f1ae2c2fcd12ca9 url: "https://pub.flutter-io.cn" source: hosted - version: "1.6.4" + version: "1.7.1" cross_file: dependency: transitive description: @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.2" + version: "2.3.3" dbus: dependency: transitive description: @@ -465,10 +465,11 @@ packages: flutter_blurhash: dependency: "direct main" description: - name: flutter_blurhash - sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" - url: "https://pub.flutter-io.cn" - source: hosted + path: "." + ref: HEAD + resolved-ref: d6c526d2570bf066c7c0ed814a4a82d6492e1bff + url: "https://github.com/MominRaza/flutter_blurhash" + source: git version: "0.7.0" flutter_colorpicker: dependency: "direct main" @@ -847,10 +848,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.flutter-io.cn" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -999,10 +1000,10 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 url: "https://pub.flutter-io.cn" source: hosted - version: "5.4.0" + version: "6.0.1" pigment: dependency: "direct main" description: @@ -1268,10 +1269,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.flutter-io.cn" source: hosted - version: "1.11.0" + version: "1.11.1" state_notifier: dependency: transitive description: @@ -1284,10 +1285,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1324,26 +1325,26 @@ packages: dependency: transitive description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.flutter-io.cn" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.3" + version: "0.5.9" test_process: dependency: transitive description: @@ -1360,6 +1361,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" + two_dimensional_scrollables: + dependency: "direct main" + description: + name: two_dimensional_scrollables + sha256: "5c3d971cd9eff7d730f7e350355eb5fa7c75e2f0d87228eef23208f4cb4910aa" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.4" typed_data: dependency: transitive description: @@ -1476,10 +1485,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.flutter-io.cn" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -1492,10 +1501,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1516,10 +1525,10 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.flutter-io.cn" source: hosted - version: "5.0.9" + version: "5.1.0" win32_registry: dependency: transitive description: @@ -1548,10 +1557,10 @@ packages: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 url: "https://pub.flutter-io.cn" source: hosted - version: "6.3.0" + version: "6.4.2" yaml: dependency: "direct main" description: @@ -1561,5 +1570,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index a1a85f2..d1d6fda 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,7 +48,9 @@ dependencies: hive_flutter: ^1.1.0 qr_flutter: ^4.1.0 blurhash_dart: ^1.2.1 - flutter_blurhash: ^0.7.0 + flutter_blurhash: + git: + url: https://github.com/MominRaza/flutter_blurhash image_picker: ^1.0.4 device_info_plus: ^9.1.0 network_info_plus: ^4.1.0 @@ -73,6 +75,7 @@ dependencies: connectivity_plus: ^5.0.1 flutter_js: ^0.8.0 google_fonts: ^6.1.0 + two_dimensional_scrollables: ^0.0.4 dev_dependencies: flutter_test: