From 3dfddcdbe2af4a098e566a50212ff8f0d5d8094d Mon Sep 17 00:00:00 2001 From: wanghongenpin Date: Fri, 27 Sep 2024 13:27:15 +0800 Subject: [PATCH] Add quick edit script to request details page --- lib/network/components/script_manager.dart | 5 +-- lib/storage/favorites.dart | 2 +- lib/ui/component/share.dart | 20 ++++++++++++ lib/ui/desktop/left_menus/favorite.dart | 14 +++++++- .../desktop/request/model/search_model.dart | 2 +- lib/ui/desktop/request/request.dart | 16 +++++++++- lib/ui/desktop/request/search.dart | 2 +- lib/ui/desktop/request/search_condition.dart | 2 +- lib/ui/desktop/toolbar/setting/script.dart | 7 ++-- lib/ui/desktop/toolbar/ssl/ssl.dart | 1 + lib/ui/mobile/menu/menu.dart | 12 +++++++ lib/ui/mobile/mobile.dart | 26 +++++++++------ lib/ui/mobile/request/favorite.dart | 2 +- lib/ui/mobile/request/request.dart | 2 +- lib/ui/mobile/request/search.dart | 32 +++++++++++-------- lib/ui/mobile/setting/script.dart | 8 +++-- lib/ui/mobile/setting/ssl.dart | 3 +- 17 files changed, 117 insertions(+), 39 deletions(-) diff --git a/lib/network/components/script_manager.dart b/lib/network/components/script_manager.dart index 3852a77..6b85118 100644 --- a/lib/network/components/script_manager.dart +++ b/lib/network/components/script_manager.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import 'package:flutter_js/flutter_js.dart'; import 'package:network_proxy/network/http/http.dart'; import 'package:network_proxy/network/http/http_headers.dart'; import 'package:network_proxy/network/util/lists.dart'; +import 'package:network_proxy/network/util/random.dart'; import 'package:network_proxy/ui/component/device.dart'; import 'package:network_proxy/network/util/logger.dart'; import 'package:path_provider/path_provider.dart'; @@ -179,7 +180,7 @@ async function onResponse(context, request, response) { ///添加脚本 Future addScript(ScriptItem item, String script) async { final path = await homePath(); - String scriptPath = "${separator}scripts$separator${DateTime.now().millisecondsSinceEpoch}.js"; + String scriptPath = "${separator}scripts$separator${RandomUtil.randomString(16)}.js"; var file = File(path + scriptPath); await file.create(recursive: true); file.writeAsString(script); diff --git a/lib/storage/favorites.dart b/lib/storage/favorites.dart index 84f26e6..8e3c7ab 100644 --- a/lib/storage/favorites.dart +++ b/lib/storage/favorites.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/ui/component/share.dart b/lib/ui/component/share.dart index 96be61b..cb3110f 100644 --- a/lib/ui/component/share.dart +++ b/lib/ui/component/share.dart @@ -2,11 +2,14 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:flutter_js/quickjs/ffi.dart'; import 'package:flutter_toastr/flutter_toastr.dart'; import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/components/script_manager.dart'; import 'package:network_proxy/network/http/http.dart'; import 'package:network_proxy/ui/component/utils.dart'; import 'package:network_proxy/ui/mobile/request/request_editor.dart'; +import 'package:network_proxy/ui/mobile/setting/script.dart'; import 'package:network_proxy/utils/curl.dart'; import 'package:share_plus/share_plus.dart'; @@ -65,6 +68,23 @@ class ShareWidget extends StatelessWidget { builder: (context) => MobileRequestEditor(request: request, proxyServer: proxyServer))); }); }), + PopupMenuItem( + child: Text(localizations.script), + onTap: () { + WidgetsBinding.instance.addPostFrameCallback((_) async { + var scriptManager = await ScriptManager.instance; + + var url = '${request?.remoteDomain()}${request?.path()}'; + var scriptItem = (scriptManager).list.firstWhereOrNull((it) => it.url == url); + String? script = scriptItem == null ? null : await scriptManager.getScript(scriptItem); + + if (!context.mounted) return; + + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => + ScriptEdit(scriptItem: scriptItem, script: script, url: scriptItem?.url ?? url))); + }); + }), ]); }); } diff --git a/lib/ui/desktop/left_menus/favorite.dart b/lib/ui/desktop/left_menus/favorite.dart index 21f5f7e..3f33293 100644 --- a/lib/ui/desktop/left_menus/favorite.dart +++ b/lib/ui/desktop/left_menus/favorite.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_toastr/flutter_toastr.dart'; +import 'package:network_proxy/network/components/script_manager.dart'; import 'package:network_proxy/network/host_port.dart'; import 'package:network_proxy/network/http/http.dart'; import 'package:network_proxy/network/http_client.dart'; @@ -33,6 +34,7 @@ import 'package:network_proxy/ui/component/utils.dart'; import 'package:network_proxy/ui/component/widgets.dart'; import 'package:network_proxy/ui/content/panel.dart'; import 'package:network_proxy/ui/desktop/request/repeat.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/script.dart'; import 'package:network_proxy/utils/curl.dart'; import 'package:network_proxy/utils/lang.dart'; import 'package:network_proxy/utils/python.dart'; @@ -178,6 +180,16 @@ class _FavoriteItemState extends State<_FavoriteItem> { requestEdit(request); }); }), + popupItem(localizations.script, onTap: () async { + var scriptManager = await ScriptManager.instance; + var url = '${request.remoteDomain()}${request.path()}'; + var scriptItem = (scriptManager).list.firstWhereOrNull((it) => it.url == url); + + String? script = scriptItem == null ? null : await scriptManager.getScript(scriptItem); + if (!mounted) return; + showDialog( + context: context, builder: (context) => ScriptEdit(scriptItem: scriptItem, script: script, url: url)); + }), const PopupMenuDivider(height: 0.3), popupItem(localizations.deleteFavorite, onTap: () { widget.onRemove?.call(widget.favorite); diff --git a/lib/ui/desktop/request/model/search_model.dart b/lib/ui/desktop/request/model/search_model.dart index 08dd0c2..86e667b 100644 --- a/lib/ui/desktop/request/model/search_model.dart +++ b/lib/ui/desktop/request/model/search_model.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/ui/desktop/request/request.dart b/lib/ui/desktop/request/request.dart index 27ebf6f..130bd3c 100644 --- a/lib/ui/desktop/request/request.dart +++ b/lib/ui/desktop/request/request.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import 'package:flutter_desktop_context_menu/flutter_desktop_context_menu.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_toastr/flutter_toastr.dart'; import 'package:network_proxy/network/bin/server.dart'; +import 'package:network_proxy/network/components/script_manager.dart'; import 'package:network_proxy/network/host_port.dart'; import 'package:network_proxy/network/http/http.dart'; import 'package:network_proxy/network/http_client.dart'; @@ -33,6 +34,7 @@ import 'package:network_proxy/ui/component/utils.dart'; import 'package:network_proxy/ui/component/widgets.dart'; import 'package:network_proxy/ui/content/panel.dart'; import 'package:network_proxy/ui/desktop/request/repeat.dart'; +import 'package:network_proxy/ui/desktop/toolbar/setting/script.dart'; import 'package:network_proxy/utils/curl.dart'; import 'package:network_proxy/utils/lang.dart'; import 'package:network_proxy/utils/python.dart'; @@ -176,6 +178,18 @@ class _RequestWidgetState extends State { requestEdit(); }); }), + MenuItem( + label: localizations.script, + onClick: (_) async { + var scriptManager = await ScriptManager.instance; + var url = '${widget.request.remoteDomain()}${widget.request.path()}'; + var scriptItem = (scriptManager).list.firstWhereOrNull((it) => it.url == url); + + String? script = scriptItem == null ? null : await scriptManager.getScript(scriptItem); + if (!mounted) return; + showDialog( + context: context, builder: (context) => ScriptEdit(scriptItem: scriptItem, script: script, url: url)); + }), MenuItem.separator(), MenuItem( label: localizations.favorite, diff --git a/lib/ui/desktop/request/search.dart b/lib/ui/desktop/request/search.dart index 0130d28..67b907d 100644 --- a/lib/ui/desktop/request/search.dart +++ b/lib/ui/desktop/request/search.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/ui/desktop/request/search_condition.dart b/lib/ui/desktop/request/search_condition.dart index 5e39740..1d6b9e7 100644 --- a/lib/ui/desktop/request/search_condition.dart +++ b/lib/ui/desktop/request/search_condition.dart @@ -1,5 +1,5 @@ /* - * Copyright 2024 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/ui/desktop/toolbar/setting/script.dart b/lib/ui/desktop/toolbar/setting/script.dart index ecc6b91..14dcc94 100644 --- a/lib/ui/desktop/toolbar/setting/script.dart +++ b/lib/ui/desktop/toolbar/setting/script.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -302,8 +302,9 @@ class _ScriptConsoleState extends State { class ScriptEdit extends StatefulWidget { final ScriptItem? scriptItem; final String? script; + final String? url; - const ScriptEdit({super.key, this.scriptItem, this.script}); + const ScriptEdit({super.key, this.scriptItem, this.script, this.url}); @override State createState() => _ScriptEditState(); @@ -321,7 +322,7 @@ class _ScriptEditState extends State { super.initState(); script = CodeController(language: javascript, text: widget.script ?? ScriptManager.template); nameController = TextEditingController(text: widget.scriptItem?.name); - urlController = TextEditingController(text: widget.scriptItem?.url); + urlController = TextEditingController(text: widget.scriptItem?.url ?? widget.url); } @override diff --git a/lib/ui/desktop/toolbar/ssl/ssl.dart b/lib/ui/desktop/toolbar/ssl/ssl.dart index f48cb20..a152749 100644 --- a/lib/ui/desktop/toolbar/ssl/ssl.dart +++ b/lib/ui/desktop/toolbar/ssl/ssl.dart @@ -151,6 +151,7 @@ class _SslState extends State { padding: const EdgeInsets.all(10), child: TextField( decoration: const InputDecoration( + hintStyle: TextStyle(color: Colors.grey), hintText: "Enter a password to protect p12 file", border: OutlineInputBorder(), ), diff --git a/lib/ui/mobile/menu/menu.dart b/lib/ui/mobile/menu/menu.dart index eef0aff..354cea8 100644 --- a/lib/ui/mobile/menu/menu.dart +++ b/lib/ui/mobile/menu/menu.dart @@ -96,6 +96,18 @@ class MoreMenu extends StatelessWidget { }, )), const PopupMenuDivider(height: 0), + PopupMenuItem( + height: 32, + child: ListTile( + dense: true, + leading: const Icon(Icons.search), + title: Text(localizations.search), + onTap: () async { + await Navigator.maybePop(context); + + MobileHomeState.searchStateKey.currentState?.showSearch(); + }, + )), PopupMenuItem( height: 32, child: ListTile( diff --git a/lib/ui/mobile/mobile.dart b/lib/ui/mobile/mobile.dart index 7a4eeb3..ad3dc92 100644 --- a/lib/ui/mobile/mobile.dart +++ b/lib/ui/mobile/mobile.dart @@ -59,7 +59,13 @@ class MobileHomePage extends StatefulWidget { } class MobileHomeState extends State implements EventListener, LifecycleListener { + ///请求列表key static final GlobalKey requestStateKey = GlobalKey(); + + ///搜索key + static final GlobalKey searchStateKey = GlobalKey(); + + ///请求列表容器 static final container = ListenableList(); /// 远程连接 @@ -207,15 +213,17 @@ class MobileHomeState extends State implements EventListener, Li } AppBar appBar() { - return AppBar(title: MobileSearch(onSearch: (val) => requestStateKey.currentState?.search(val)), actions: [ - IconButton( - tooltip: localizations.clear, - icon: const Icon(Icons.cleaning_services_outlined), - onPressed: () => requestStateKey.currentState?.clean()), - const SizedBox(width: 2), - MoreMenu(proxyServer: proxyServer, desktop: desktop), - const SizedBox(width: 10), - ]); + return AppBar( + title: MobileSearch(key: searchStateKey, onSearch: (val) => requestStateKey.currentState?.search(val)), + actions: [ + IconButton( + tooltip: localizations.clear, + icon: const Icon(Icons.cleaning_services_outlined), + onPressed: () => requestStateKey.currentState?.clean()), + const SizedBox(width: 2), + MoreMenu(proxyServer: proxyServer, desktop: desktop), + const SizedBox(width: 10), + ]); } FloatingActionButton _launchActionButton() { diff --git a/lib/ui/mobile/request/favorite.dart b/lib/ui/mobile/request/favorite.dart index 0ebe570..4bb1ea0 100644 --- a/lib/ui/mobile/request/favorite.dart +++ b/lib/ui/mobile/request/favorite.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/ui/mobile/request/request.dart b/lib/ui/mobile/request/request.dart index 719cc7a..87b5d5d 100644 --- a/lib/ui/mobile/request/request.dart +++ b/lib/ui/mobile/request/request.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/lib/ui/mobile/request/search.dart b/lib/ui/mobile/request/search.dart index 3253fd4..266513f 100644 --- a/lib/ui/mobile/request/search.dart +++ b/lib/ui/mobile/request/search.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,9 @@ class MobileSearch extends StatefulWidget { class MobileSearchState extends State { SearchModel searchModel = SearchModel(); - bool searched = false; - TextEditingController keywordController = TextEditingController(); - bool changing = false; + bool _searched = false; + final TextEditingController _keywordController = TextEditingController(); + bool _changing = false; @override void initState() { @@ -46,22 +46,28 @@ class MobileSearchState extends State { }); } + @override + dispose() { + _keywordController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(left: 20), child: TextFormField( - controller: keywordController, + controller: _keywordController, cursorHeight: 20, keyboardType: TextInputType.url, onTapOutside: (event) => FocusManager.instance.primaryFocus?.unfocus(), onChanged: (val) { searchModel.keyword = val; - if (!changing) { - changing = true; + if (!_changing) { + _changing = true; Future.delayed(const Duration(milliseconds: 500), () { - changing = false; - if (!searched) { + _changing = false; + if (!_searched) { searchModel.searchOptions = {Option.url, Option.method, Option.responseContentType}; } widget.onSearch?.call(searchModel); @@ -71,7 +77,7 @@ class MobileSearchState extends State { decoration: InputDecoration( border: InputBorder.none, prefixIcon: - InkWell(onTap: showSearch, child: Icon(Icons.search, color: searched ? Colors.green : Colors.blue)), + InkWell(onTap: showSearch, child: Icon(Icons.search, color: _searched ? Colors.green : Colors.blue)), hintText: 'Search'))); } @@ -81,7 +87,7 @@ class MobileSearchState extends State { isScrollControlled: true, context: context, builder: (context) { - if (!searched) { + if (!_searched) { searchModel.searchOptions = {Option.url}; } return Padding( @@ -94,8 +100,8 @@ class MobileSearchState extends State { onSearch: (val) { setState(() { searchModel = val; - searched = searchModel.isNotEmpty; - keywordController.text = searchModel.keyword ?? ''; + _searched = searchModel.isNotEmpty; + _keywordController.text = searchModel.keyword ?? ''; widget.onSearch?.call(searchModel); }); }, diff --git a/lib/ui/mobile/setting/script.dart b/lib/ui/mobile/setting/script.dart index 75db61c..02a5d43 100644 --- a/lib/ui/mobile/setting/script.dart +++ b/lib/ui/mobile/setting/script.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -376,8 +376,9 @@ class _ScriptLogSmallWindowState extends State { class ScriptEdit extends StatefulWidget { final ScriptItem? scriptItem; final String? script; + final String? url; - const ScriptEdit({super.key, this.scriptItem, this.script}); + const ScriptEdit({super.key, this.scriptItem, this.script, this.url}); @override State createState() => _ScriptEditState(); @@ -395,7 +396,7 @@ class _ScriptEditState extends State { super.initState(); script = CodeController(language: javascript, text: widget.script ?? ScriptManager.template); nameController = TextEditingController(text: widget.scriptItem?.name); - urlController = TextEditingController(text: widget.scriptItem?.url); + urlController = TextEditingController(text: widget.scriptItem?.url ?? widget.url); } @override @@ -446,6 +447,7 @@ class _ScriptEditState extends State { _refreshScript(); if (context.mounted) { + FlutterToastr.show(localizations.saveSuccess, context); Navigator.of(context).maybePop(true); } }, diff --git a/lib/ui/mobile/setting/ssl.dart b/lib/ui/mobile/setting/ssl.dart index 79c0958..587a3bb 100644 --- a/lib/ui/mobile/setting/ssl.dart +++ b/lib/ui/mobile/setting/ssl.dart @@ -1,5 +1,5 @@ /* - * Copyright 2023 WangHongEn + * Copyright 2023 Hongen Wang * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -175,6 +175,7 @@ class _MobileSslState extends State { padding: const EdgeInsets.all(10), child: TextField( decoration: const InputDecoration( + hintStyle: TextStyle(color: Colors.grey), hintText: "Enter a password to protect p12 file", border: OutlineInputBorder(), ),