mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-03-15 04:23:17 +08:00
Mobile support report server
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 55;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -524,14 +524,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
name = "[CP] Copy Pods Resources";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||||
@@ -583,14 +579,10 @@
|
|||||||
inputFileListPaths = (
|
inputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
);
|
);
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
name = "[CP] Embed Pods Frameworks";
|
||||||
outputFileListPaths = (
|
outputFileListPaths = (
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
);
|
);
|
||||||
outputPaths = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
|||||||
@@ -105,6 +105,12 @@
|
|||||||
"matchRule": "Match Rule",
|
"matchRule": "Match Rule",
|
||||||
"emptyMatchAll": "Empty means match all",
|
"emptyMatchAll": "Empty means match all",
|
||||||
"newBuilt": "New",
|
"newBuilt": "New",
|
||||||
|
"reportServers": "Report Servers",
|
||||||
|
"addReportServer": "Add Report Server",
|
||||||
|
"editReportServer": "Edit Report Server",
|
||||||
|
"serverUrl": "Server URL",
|
||||||
|
"compression": "Compression",
|
||||||
|
"compressionNone": "None",
|
||||||
"newFolder": "New Folder",
|
"newFolder": "New Folder",
|
||||||
"enableSelect": "Enable Select",
|
"enableSelect": "Enable Select",
|
||||||
"disableSelect": "Disable Select",
|
"disableSelect": "Disable Select",
|
||||||
|
|||||||
@@ -708,6 +708,42 @@ abstract class AppLocalizations {
|
|||||||
/// **'New'**
|
/// **'New'**
|
||||||
String get newBuilt;
|
String get newBuilt;
|
||||||
|
|
||||||
|
/// No description provided for @reportServers.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Report Servers'**
|
||||||
|
String get reportServers;
|
||||||
|
|
||||||
|
/// No description provided for @addReportServer.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Add Report Server'**
|
||||||
|
String get addReportServer;
|
||||||
|
|
||||||
|
/// No description provided for @editReportServer.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Edit Report Server'**
|
||||||
|
String get editReportServer;
|
||||||
|
|
||||||
|
/// No description provided for @serverUrl.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Server URL'**
|
||||||
|
String get serverUrl;
|
||||||
|
|
||||||
|
/// No description provided for @compression.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Compression'**
|
||||||
|
String get compression;
|
||||||
|
|
||||||
|
/// No description provided for @compressionNone.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'None'**
|
||||||
|
String get compressionNone;
|
||||||
|
|
||||||
/// No description provided for @newFolder.
|
/// No description provided for @newFolder.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -316,6 +316,24 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newBuilt => 'New';
|
String get newBuilt => 'New';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get reportServers => 'Report Servers';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get addReportServer => 'Add Report Server';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get editReportServer => 'Edit Report Server';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get serverUrl => 'Server URL';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get compression => 'Compression';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get compressionNone => 'None';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get newFolder => 'New Folder';
|
String get newFolder => 'New Folder';
|
||||||
|
|
||||||
|
|||||||
@@ -316,6 +316,24 @@ class AppLocalizationsZh extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get newBuilt => '新建';
|
String get newBuilt => '新建';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get reportServers => '上报服务器';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get addReportServer => '新增上报服务器';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get editReportServer => '编辑上报服务器';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get serverUrl => '服务器 URL';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get compression => '压缩';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get compressionNone => '无';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get newFolder => '新建文件夹';
|
String get newFolder => '新建文件夹';
|
||||||
|
|
||||||
@@ -1310,6 +1328,24 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
|
|||||||
@override
|
@override
|
||||||
String get newBuilt => '新建';
|
String get newBuilt => '新建';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get reportServers => '上報伺服器';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get addReportServer => '新增上報伺服器';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get editReportServer => '編輯上報伺服器';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get serverUrl => '伺服器 URL';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get compression => '壓縮';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get compressionNone => '無';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get newFolder => '新建資料夾';
|
String get newFolder => '新建資料夾';
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,12 @@
|
|||||||
"matchRule": "匹配规则",
|
"matchRule": "匹配规则",
|
||||||
"emptyMatchAll": "为空表示匹配全部",
|
"emptyMatchAll": "为空表示匹配全部",
|
||||||
"newBuilt": "新建",
|
"newBuilt": "新建",
|
||||||
|
"reportServers": "上报服务器",
|
||||||
|
"addReportServer": "新增上报服务器",
|
||||||
|
"editReportServer": "编辑上报服务器",
|
||||||
|
"serverUrl": "服务器 URL",
|
||||||
|
"compression": "压缩",
|
||||||
|
"compressionNone": "无",
|
||||||
"newFolder": "新建文件夹",
|
"newFolder": "新建文件夹",
|
||||||
"enableSelect": "启用选择",
|
"enableSelect": "启用选择",
|
||||||
"disableSelect": "禁用选择",
|
"disableSelect": "禁用选择",
|
||||||
|
|||||||
@@ -103,6 +103,12 @@
|
|||||||
"matchRule": "符合規則",
|
"matchRule": "符合規則",
|
||||||
"emptyMatchAll": "為空表示符合全部",
|
"emptyMatchAll": "為空表示符合全部",
|
||||||
"newBuilt": "新建",
|
"newBuilt": "新建",
|
||||||
|
"reportServers": "上報伺服器",
|
||||||
|
"addReportServer": "新增上報伺服器",
|
||||||
|
"editReportServer": "編輯上報伺服器",
|
||||||
|
"serverUrl": "伺服器 URL",
|
||||||
|
"compression": "壓縮",
|
||||||
|
"compressionNone": "無",
|
||||||
"newFolder": "新建資料夾",
|
"newFolder": "新建資料夾",
|
||||||
"enableSelect": "啟用選擇",
|
"enableSelect": "啟用選擇",
|
||||||
"disableSelect": "停用選擇",
|
"disableSelect": "停用選擇",
|
||||||
|
|||||||
@@ -96,9 +96,6 @@ class ReportServer {
|
|||||||
/// 压缩方式:none/gzip,默认 none
|
/// 压缩方式:none/gzip,默认 none
|
||||||
final String? compression;
|
final String? compression;
|
||||||
|
|
||||||
/// 额外请求头(可选)
|
|
||||||
final Map<String, String>? headers;
|
|
||||||
|
|
||||||
RegExp _urlReg;
|
RegExp _urlReg;
|
||||||
|
|
||||||
ReportServer({
|
ReportServer({
|
||||||
@@ -107,7 +104,6 @@ class ReportServer {
|
|||||||
required this.serverUrl,
|
required this.serverUrl,
|
||||||
this.enabled = true,
|
this.enabled = true,
|
||||||
this.compression,
|
this.compression,
|
||||||
this.headers,
|
|
||||||
}) : _urlReg = RegExp(matchUrl.replaceAll("*", ".*").replaceFirst('?', '\\?'));
|
}) : _urlReg = RegExp(matchUrl.replaceAll("*", ".*").replaceFirst('?', '\\?'));
|
||||||
|
|
||||||
bool match(String url) {
|
bool match(String url) {
|
||||||
@@ -136,19 +132,16 @@ class ReportServer {
|
|||||||
serverUrl: serverUrl ?? this.serverUrl,
|
serverUrl: serverUrl ?? this.serverUrl,
|
||||||
enabled: enabled ?? this.enabled,
|
enabled: enabled ?? this.enabled,
|
||||||
compression: compression ?? this.compression,
|
compression: compression ?? this.compression,
|
||||||
headers: headers ?? this.headers,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ReportServer.fromJson(Map<String, dynamic> json) {
|
factory ReportServer.fromJson(Map<String, dynamic> json) {
|
||||||
final headers = json['headers'];
|
|
||||||
return ReportServer(
|
return ReportServer(
|
||||||
name: json['name'] ?? '',
|
name: json['name'] ?? '',
|
||||||
matchUrl: json['matchUrl'] ?? '',
|
matchUrl: json['matchUrl'] ?? '',
|
||||||
serverUrl: json['serverUrl'] ?? '',
|
serverUrl: json['serverUrl'] ?? '',
|
||||||
enabled: json['enabled'] ?? true,
|
enabled: json['enabled'] ?? true,
|
||||||
compression: (json['compression'] ?? 'none') as String,
|
compression: (json['compression'] ?? 'none') as String,
|
||||||
headers: headers == null ? null : Map<String, String>.from(headers as Map),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class _ReportServersPageState extends State<ReportServersPage> {
|
|||||||
|
|
||||||
Widget labeled(String label, Widget field, {bool expanded = true}) => Row(
|
Widget labeled(String label, Widget field, {bool expanded = true}) => Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 85, child: Text(label)),
|
SizedBox(width: AppLocalizations.of(context)!.localeName == 'en' ? 95 : 85, child: Text(label)),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
expanded ? Expanded(child: field) : field,
|
expanded ? Expanded(child: field) : field,
|
||||||
],
|
],
|
||||||
@@ -158,7 +158,8 @@ class _ReportServersPageState extends State<ReportServersPage> {
|
|||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (!(formKey.currentState as FormState).validate()) {
|
if (!(formKey.currentState as FormState).validate()) {
|
||||||
FlutterToastr.show("${localizations.serverUrl} ${localizations.cannotBeEmpty}", context, position: FlutterToastr.top);
|
FlutterToastr.show("${localizations.serverUrl} ${localizations.cannotBeEmpty}", context,
|
||||||
|
position: FlutterToastr.top);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'package:proxypin/l10n/app_localizations.dart';
|
|||||||
import 'package:proxypin/network/bin/server.dart';
|
import 'package:proxypin/network/bin/server.dart';
|
||||||
import 'package:proxypin/ui/mobile/mobile.dart';
|
import 'package:proxypin/ui/mobile/mobile.dart';
|
||||||
import 'package:proxypin/ui/mobile/setting/app_filter.dart';
|
import 'package:proxypin/ui/mobile/setting/app_filter.dart';
|
||||||
|
import 'package:proxypin/ui/mobile/setting/report_servers.dart';
|
||||||
import 'package:proxypin/ui/mobile/setting/ssl.dart';
|
import 'package:proxypin/ui/mobile/setting/ssl.dart';
|
||||||
import 'package:proxypin/ui/mobile/widgets/highlight.dart';
|
import 'package:proxypin/ui/mobile/widgets/highlight.dart';
|
||||||
import 'package:proxypin/ui/mobile/widgets/remote_device.dart';
|
import 'package:proxypin/ui/mobile/widgets/remote_device.dart';
|
||||||
@@ -77,6 +78,17 @@ class MoreMenu extends StatelessWidget {
|
|||||||
navigator(context, RemoteDevicePage(proxyServer: proxyServer, remoteDevice: remoteDevice));
|
navigator(context, RemoteDevicePage(proxyServer: proxyServer, remoteDevice: remoteDevice));
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
|
PopupMenuItem(
|
||||||
|
height: 32,
|
||||||
|
child: ListTile(
|
||||||
|
dense: true,
|
||||||
|
leading: const Icon(Icons.cloud_upload_outlined),
|
||||||
|
title: Text(localizations.reportServers),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.maybePop(context);
|
||||||
|
navigator(context, const ReportServersPageMobile());
|
||||||
|
},
|
||||||
|
)),
|
||||||
const PopupMenuDivider(height: 0),
|
const PopupMenuDivider(height: 0),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
height: 32,
|
height: 32,
|
||||||
|
|||||||
@@ -443,7 +443,7 @@ class RequestPageState extends State<RequestPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 检查远程连接
|
/// 检查远程连接
|
||||||
checkConnectTask(BuildContext context) async {
|
Future<void> checkConnectTask(BuildContext context) async {
|
||||||
int retry = 0;
|
int retry = 0;
|
||||||
Timer.periodic(const Duration(milliseconds: 15000), (timer) async {
|
Timer.periodic(const Duration(milliseconds: 15000), (timer) async {
|
||||||
if (remoteDevice.value.connect == false) {
|
if (remoteDevice.value.connect == false) {
|
||||||
|
|||||||
273
lib/ui/mobile/setting/report_servers.dart
Normal file
273
lib/ui/mobile/setting/report_servers.dart
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
* Mobile report servers page
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||||
|
import 'package:proxypin/network/components/manager/report_server_manager.dart';
|
||||||
|
import 'package:proxypin/ui/component/widgets.dart';
|
||||||
|
import 'package:proxypin/ui/component/utils.dart';
|
||||||
|
import '../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
class ReportServersPageMobile extends StatefulWidget {
|
||||||
|
const ReportServersPageMobile({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReportServersPageMobile> createState() => _ReportServersPageMobileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReportServersPageMobileState extends State<ReportServersPageMobile> {
|
||||||
|
List<ReportServer> _servers = [];
|
||||||
|
bool _loading = true;
|
||||||
|
|
||||||
|
AppLocalizations get localizations => AppLocalizations.of(context)!;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _load() async {
|
||||||
|
final manager = await ReportServerManager.instance;
|
||||||
|
setState(() {
|
||||||
|
_servers = List.of(manager.servers);
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ReportServer?> _showServerDialog({ReportServer? initial}) async {
|
||||||
|
// Push the edit page and return the created/edited ReportServer
|
||||||
|
final result = await Navigator.of(context).push<ReportServer>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (ctx) => ReportServerEditPageMobile(initial: initial),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _addServer() async {
|
||||||
|
final server = await _showServerDialog();
|
||||||
|
if (server != null) {
|
||||||
|
final manager = await ReportServerManager.instance;
|
||||||
|
await manager.add(server);
|
||||||
|
await _load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _editServer(int index) async {
|
||||||
|
final initial = _servers[index];
|
||||||
|
final server = await _showServerDialog(initial: initial);
|
||||||
|
if (server != null) {
|
||||||
|
final manager = await ReportServerManager.instance;
|
||||||
|
await manager.update(index, server);
|
||||||
|
await _load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _confirmDelete(int index) async {
|
||||||
|
showConfirmDialog(context, onConfirm: () async {
|
||||||
|
final manager = await ReportServerManager.instance;
|
||||||
|
await manager.removeAt(index);
|
||||||
|
await _load();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(localizations.reportServers, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
TextButton.icon(
|
||||||
|
label: Text(localizations.add),
|
||||||
|
onPressed: _addServer,
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: _loading
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: _servers.isEmpty
|
||||||
|
? Center(child: Text(localizations.emptyData))
|
||||||
|
: ListView.separated(
|
||||||
|
itemCount: _servers.length,
|
||||||
|
separatorBuilder: (_, __) => const Divider(height: 0, thickness: 0.3),
|
||||||
|
itemBuilder: (ctx, idx) {
|
||||||
|
final s = _servers[idx];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0),
|
||||||
|
leading: SizedBox(
|
||||||
|
width: 32,
|
||||||
|
child: Checkbox(
|
||||||
|
value: s.enabled,
|
||||||
|
onChanged: (v) async {
|
||||||
|
final manager = await ReportServerManager.instance;
|
||||||
|
await manager.toggleEnabled(idx, v == true);
|
||||||
|
await _load();
|
||||||
|
})),
|
||||||
|
title: Text(s.name.isEmpty ? '-' : s.name),
|
||||||
|
subtitle: Text(s.serverUrl),
|
||||||
|
trailing: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// IconButton(
|
||||||
|
// onPressed: () => _editServer(idx), icon: const Icon(Icons.edit_outlined, size: 23)),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => _confirmDelete(idx), icon: const Icon(Icons.delete_outline, size: 23)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () => _editServer(idx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A standalone page for adding / editing a ReportServer on mobile.
|
||||||
|
class ReportServerEditPageMobile extends StatefulWidget {
|
||||||
|
final ReportServer? initial;
|
||||||
|
|
||||||
|
const ReportServerEditPageMobile({super.key, this.initial});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ReportServerEditPageMobile> createState() => _ReportServerEditPageMobileState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ReportServerEditPageMobileState extends State<ReportServerEditPageMobile> {
|
||||||
|
late TextEditingController _nameCtrl;
|
||||||
|
late TextEditingController _matchUrlCtrl;
|
||||||
|
late TextEditingController _serverUrlCtrl;
|
||||||
|
String _compression = 'none';
|
||||||
|
bool _enabled = true;
|
||||||
|
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final init = widget.initial;
|
||||||
|
_nameCtrl = TextEditingController(text: init?.name ?? '');
|
||||||
|
_matchUrlCtrl = TextEditingController(text: init?.matchUrl ?? '');
|
||||||
|
_serverUrlCtrl = TextEditingController(text: init?.serverUrl ?? '');
|
||||||
|
_compression = init?.compression ?? 'none';
|
||||||
|
_enabled = init?.enabled ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDecoration dec({String? hint}) => InputDecoration(
|
||||||
|
hintText: hint,
|
||||||
|
hintStyle: TextStyle(color: Colors.grey.shade500, fontSize: 14),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
|
||||||
|
focusedBorder:
|
||||||
|
OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).colorScheme.primary, width: 2)),
|
||||||
|
isDense: true,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget labeled(String label, Widget field, {bool expanded = true}) => Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: AppLocalizations.of(context)!.localeName == 'en' ? 95 : 85, child: Text(label)),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
expanded ? Expanded(child: field) : field,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
void _onSave() {
|
||||||
|
if (!(_formKey.currentState as FormState).validate()) {
|
||||||
|
FlutterToastr.show(
|
||||||
|
"${AppLocalizations.of(context)!.serverUrl} ${AppLocalizations.of(context)!.cannotBeEmpty}", context,
|
||||||
|
position: FlutterToastr.top);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverUrl = _serverUrlCtrl.text.trim();
|
||||||
|
if (!serverUrl.startsWith('http://') && !serverUrl.startsWith('https://')) {
|
||||||
|
serverUrl = 'http://$serverUrl';
|
||||||
|
}
|
||||||
|
|
||||||
|
final server = ReportServer(
|
||||||
|
name: _nameCtrl.text.trim(),
|
||||||
|
matchUrl: _matchUrlCtrl.text.trim(),
|
||||||
|
serverUrl: serverUrl,
|
||||||
|
enabled: _enabled,
|
||||||
|
compression: _compression,
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigator.of(context).pop(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(widget.initial == null ? localizations.addReportServer : localizations.editReportServer),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: _onSave, child: Text(localizations.save)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
labeled('${localizations.name}: ',
|
||||||
|
TextField(controller: _nameCtrl, decoration: dec(hint: localizations.pleaseEnter))),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
labeled(
|
||||||
|
'${localizations.match} URL: ',
|
||||||
|
TextFormField(
|
||||||
|
controller: _matchUrlCtrl,
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
validator: (v) => v?.isNotEmpty == true ? null : "",
|
||||||
|
decoration: dec(hint: 'https://example.com/api/*')),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
labeled(
|
||||||
|
'${localizations.serverUrl}: ',
|
||||||
|
TextFormField(
|
||||||
|
controller: _serverUrlCtrl,
|
||||||
|
keyboardType: TextInputType.url,
|
||||||
|
validator: (v) => v?.isNotEmpty == true ? null : "",
|
||||||
|
decoration: dec(hint: 'http://example.com/report')),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
labeled(
|
||||||
|
'${localizations.compression}: ',
|
||||||
|
expanded: false,
|
||||||
|
SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
initialValue: _compression,
|
||||||
|
decoration: dec(),
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem(value: 'none', child: Text(localizations.compressionNone)),
|
||||||
|
DropdownMenuItem(value: 'gzip', child: Text('GZIP')),
|
||||||
|
],
|
||||||
|
onChanged: (v) => setState(() => _compression = v ?? 'none'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
labeled(
|
||||||
|
'${localizations.enable}: ',
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: SwitchWidget(value: _enabled, scale: 0.9, onChanged: (v) => setState(() => _enabled = v))),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user