mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-06-01 17:15:48 +08:00
220 lines
9.0 KiB
Dart
220 lines
9.0 KiB
Dart
/*
|
||
* 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.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* https://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:proxypin/l10n/app_localizations.dart';
|
||
import 'package:proxypin/network/bin/configuration.dart';
|
||
import 'package:proxypin/network/bin/listener.dart';
|
||
import 'package:proxypin/network/bin/server.dart';
|
||
import 'package:proxypin/network/channel/channel.dart';
|
||
import 'package:proxypin/network/channel/channel_context.dart';
|
||
import 'package:proxypin/network/http/http.dart';
|
||
import 'package:proxypin/network/http/websocket.dart';
|
||
import 'package:proxypin/storage/histories.dart';
|
||
import 'package:proxypin/ui/component/memory_cleanup.dart';
|
||
import 'package:proxypin/ui/component/widgets.dart';
|
||
import 'package:proxypin/ui/configuration.dart';
|
||
import 'package:proxypin/ui/content/panel.dart';
|
||
import 'package:proxypin/ui/desktop/left_menus/favorite.dart';
|
||
import 'package:proxypin/ui/desktop/left_menus/history.dart';
|
||
import 'package:proxypin/ui/desktop/left_menus/navigation.dart';
|
||
import 'package:proxypin/ui/desktop/request/list.dart';
|
||
import 'package:proxypin/ui/desktop/toolbar/toolbar.dart';
|
||
import 'package:proxypin/ui/desktop/widgets/windows_toolbar.dart';
|
||
import 'package:proxypin/utils/listenable_list.dart';
|
||
|
||
import '../app_update/app_update_repository.dart';
|
||
import '../component/split_view.dart';
|
||
import '../toolbox/toolbox.dart';
|
||
|
||
/// @author wanghongen
|
||
/// 2023/10/8
|
||
class DesktopHomePage extends StatefulWidget {
|
||
final Configuration configuration;
|
||
final AppConfiguration appConfiguration;
|
||
|
||
const DesktopHomePage(this.configuration, this.appConfiguration, {super.key, required});
|
||
|
||
@override
|
||
State<DesktopHomePage> createState() => _DesktopHomePagePageState();
|
||
}
|
||
|
||
class _DesktopHomePagePageState extends State<DesktopHomePage> implements EventListener {
|
||
static final container = ListenableList<HttpRequest>();
|
||
|
||
static final GlobalKey<DesktopRequestListState> requestListStateKey = GlobalKey<DesktopRequestListState>();
|
||
|
||
final ValueNotifier<int> _selectIndex = ValueNotifier(0);
|
||
StreamSubscription<HistoryItem>? _remoteHistorySubscription;
|
||
|
||
late ProxyServer proxyServer = ProxyServer(widget.configuration);
|
||
late NetworkTabController panel;
|
||
|
||
AppLocalizations get localizations => AppLocalizations.of(context)!;
|
||
|
||
@override
|
||
void onRequest(Channel channel, HttpRequest request) {
|
||
requestListStateKey.currentState!.add(channel, request);
|
||
|
||
if (request.attributes['quickShare'] == true) {
|
||
_selectIndex.value = 0;
|
||
panel.change(request, request.response);
|
||
}
|
||
|
||
//监控内存 到达阈值清理
|
||
MemoryCleanupMonitor.onMonitor(onCleanup: () {
|
||
requestListStateKey.currentState?.cleanupEarlyData(32);
|
||
});
|
||
}
|
||
|
||
@override
|
||
void onResponse(ChannelContext channelContext, HttpResponse response) {
|
||
requestListStateKey.currentState!.addResponse(channelContext, response);
|
||
}
|
||
|
||
@override
|
||
void onMessage(Channel channel, HttpMessage message, WebSocketFrame frame) {
|
||
if (panel.request.get() == message || panel.response.get() == message) {
|
||
panel.changeState();
|
||
}
|
||
}
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
proxyServer.addListener(this);
|
||
panel = NetworkTabController(tabStyle: const TextStyle(fontSize: 16), proxyServer: proxyServer);
|
||
_remoteHistorySubscription = HistoryStorage.onRemoteImported.listen((_) {
|
||
if (mounted) {
|
||
_selectIndex.value = 2;
|
||
}
|
||
});
|
||
|
||
if (widget.appConfiguration.upgradeNoticeV27) {
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
showUpgradeNotice();
|
||
});
|
||
} else {
|
||
AppUpdateRepository.checkUpdate(context);
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_remoteHistorySubscription?.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
var navigationView = [
|
||
DesktopRequestListWidget(key: requestListStateKey, proxyServer: proxyServer, list: container, panel: panel),
|
||
Favorites(panel: panel),
|
||
HistoryPageWidget(proxyServer: proxyServer, container: container, panel: panel),
|
||
const Toolbox()
|
||
];
|
||
|
||
return Scaffold(
|
||
appBar: Tab(
|
||
child: Container(
|
||
padding: EdgeInsets.only(bottom: 2.5),
|
||
margin: EdgeInsets.only(bottom: 5),
|
||
decoration: BoxDecoration(
|
||
// color: Theme.of(context).brightness == Brightness.dark ? null : Color(0xFFF9F9F9),
|
||
border: Border(
|
||
bottom: BorderSide(
|
||
color: Theme.of(context).dividerColor.withValues(alpha: 0.3),
|
||
width: Platform.isMacOS ? 0.2 : 0.55))),
|
||
child: Platform.isMacOS
|
||
? Toolbar(proxyServer, requestListStateKey)
|
||
: WindowsToolbar(title: Toolbar(proxyServer, requestListStateKey)),
|
||
)),
|
||
body: Row(
|
||
children: [
|
||
LeftNavigationBar(
|
||
selectIndex: _selectIndex, appConfiguration: widget.appConfiguration, proxyServer: proxyServer),
|
||
Expanded(
|
||
child: VerticalSplitView(
|
||
ratio: widget.appConfiguration.panelRatio,
|
||
minRatio: 0.15,
|
||
maxRatio: 0.9,
|
||
onRatioChanged: (ratio) {
|
||
widget.appConfiguration.panelRatio = double.parse(ratio.toStringAsFixed(2));
|
||
widget.appConfiguration.flushConfig();
|
||
},
|
||
left: ValueListenableBuilder(
|
||
valueListenable: _selectIndex,
|
||
builder: (_, index, __) =>
|
||
LazyIndexedStack(index: index < 0 ? 0 : index, children: navigationView)),
|
||
right: panel),
|
||
)
|
||
],
|
||
));
|
||
}
|
||
|
||
//更新引导
|
||
void showUpgradeNotice() {
|
||
bool isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh');
|
||
|
||
showDialog(
|
||
context: context,
|
||
barrierDismissible: false,
|
||
builder: (_) {
|
||
return AlertDialog(
|
||
scrollable: true,
|
||
actions: [
|
||
TextButton(
|
||
onPressed: () {
|
||
widget.appConfiguration.upgradeNoticeV27 = false;
|
||
widget.appConfiguration.flushConfig();
|
||
Navigator.pop(context);
|
||
},
|
||
child: Text(localizations.close))
|
||
],
|
||
title: Text(isCN ? '更新内容V${AppConfiguration.version}' : "What's new in V${AppConfiguration.version}",
|
||
style: const TextStyle(fontSize: 18)),
|
||
content: Container(
|
||
constraints: const BoxConstraints(maxWidth: 600),
|
||
child: SelectableText(
|
||
isCN
|
||
? '提示:默认不会开启HTTPS抓包,请安装证书后再开启HTTPS抓包。\n'
|
||
'点击HTTPS抓包(加锁图标),选择安装根证书,按照提示操作即可。\n\n'
|
||
'1. 新增html、css、js格式化以及代码高亮;\n'
|
||
'2. 高级重发支持指定时间;\n'
|
||
'3. 域名列表增加导出har文件;\n'
|
||
'4. 远程设备增加快速分享;\n'
|
||
'5. 收藏支持websocket消息持久化;\n'
|
||
'6. 远程脚本加载添加引导;\n'
|
||
'7. 优化消息体大文本展示;\n'
|
||
'8. 修复自定已读状态丢失问题;\n'
|
||
: 'Note: HTTPS capture is disabled by default — please install the certificate before enabling HTTPS capture.\n'
|
||
'Click the HTTPS capture (lock) icon, choose "Install Root Certificate", and follow the prompts to complete installation.\n\n'
|
||
'1. Added HTML, CSS, and JS formatting with code highlighting;\n'
|
||
'2. Advanced repeat now supports specifying the time;\n'
|
||
'3. Added HAR file export for domain list;\n'
|
||
'4. Added quick share for remote devices;\n'
|
||
'5. Favorites support WebSocket message persistence;\n'
|
||
'6. Added guidance for remote script loading;\n'
|
||
'7. Optimized large text display in message body;\n'
|
||
'8. Fixed issue where custom read status was lost;\n',
|
||
style: const TextStyle(fontSize: 14))));
|
||
});
|
||
}
|
||
}
|