mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-05-07 00:15:55 +08:00
Add quick edit script to request details page
This commit is contained in:
@@ -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<void> 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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)));
|
||||
});
|
||||
}),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<RequestWidget> {
|
||||
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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<ScriptConsoleWidget> {
|
||||
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<StatefulWidget> createState() => _ScriptEditState();
|
||||
@@ -321,7 +322,7 @@ class _ScriptEditState extends State<ScriptEdit> {
|
||||
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
|
||||
|
||||
@@ -151,6 +151,7 @@ class _SslState extends State<SslWidget> {
|
||||
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(),
|
||||
),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -59,7 +59,13 @@ class MobileHomePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class MobileHomeState extends State<MobileHomePage> implements EventListener, LifecycleListener {
|
||||
///请求列表key
|
||||
static final GlobalKey<RequestListState> requestStateKey = GlobalKey<RequestListState>();
|
||||
|
||||
///搜索key
|
||||
static final GlobalKey<MobileSearchState> searchStateKey = GlobalKey<MobileSearchState>();
|
||||
|
||||
///请求列表容器
|
||||
static final container = ListenableList<HttpRequest>();
|
||||
|
||||
/// 远程连接
|
||||
@@ -207,15 +213,17 @@ class MobileHomeState extends State<MobileHomePage> 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() {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<MobileSearch> {
|
||||
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<MobileSearch> {
|
||||
});
|
||||
}
|
||||
|
||||
@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<MobileSearch> {
|
||||
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<MobileSearch> {
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
if (!searched) {
|
||||
if (!_searched) {
|
||||
searchModel.searchOptions = {Option.url};
|
||||
}
|
||||
return Padding(
|
||||
@@ -94,8 +100,8 @@ class MobileSearchState extends State<MobileSearch> {
|
||||
onSearch: (val) {
|
||||
setState(() {
|
||||
searchModel = val;
|
||||
searched = searchModel.isNotEmpty;
|
||||
keywordController.text = searchModel.keyword ?? '';
|
||||
_searched = searchModel.isNotEmpty;
|
||||
_keywordController.text = searchModel.keyword ?? '';
|
||||
widget.onSearch?.call(searchModel);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -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<ScriptLogSmallWindow> {
|
||||
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<StatefulWidget> createState() => _ScriptEditState();
|
||||
@@ -395,7 +396,7 @@ class _ScriptEditState extends State<ScriptEdit> {
|
||||
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<ScriptEdit> {
|
||||
|
||||
_refreshScript();
|
||||
if (context.mounted) {
|
||||
FlutterToastr.show(localizations.saveSuccess, context);
|
||||
Navigator.of(context).maybePop(true);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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<MobileSslWidget> {
|
||||
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(),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user