Files
proxypin/lib/ui/desktop/desktop.dart
wanghongenpin 3eb4986496 1.2.7-beta
2026-04-12 18:47:18 +08:00

220 lines
9.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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))));
});
}
}