Merge pull request #137 from lollipopkit/lollipopkit/issue136

Lollipopkit/issue136
This commit is contained in:
lollipopkit
2023-08-18 20:08:30 +08:00
committed by GitHub
43 changed files with 353 additions and 237 deletions

View File

@@ -566,6 +566,12 @@ abstract class S {
/// **'Getting token...'**
String get gettingToken;
/// No description provided for @goBackQ.
///
/// In en, this message translates to:
/// **'Go back?'**
String get goBackQ;
/// No description provided for @goto.
///
/// In en, this message translates to:

View File

@@ -254,6 +254,9 @@ class SDe extends S {
@override
String get gettingToken => 'Getting token...';
@override
String get goBackQ => 'Zurückkommen?';
@override
String get goto => 'Pfad öffnen';

View File

@@ -254,6 +254,9 @@ class SEn extends S {
@override
String get gettingToken => 'Getting token...';
@override
String get goBackQ => 'Go back?';
@override
String get goto => 'Go to';

View File

@@ -254,6 +254,9 @@ class SId extends S {
@override
String get gettingToken => 'Mendapatkan token ...';
@override
String get goBackQ => 'Datang kembali?';
@override
String get goto => 'Pergi ke';

View File

@@ -254,6 +254,9 @@ class SZh extends S {
@override
String get gettingToken => '正在获取Token...';
@override
String get goBackQ => '返回?';
@override
String get goto => '前往';
@@ -953,6 +956,9 @@ class SZhTw extends SZh {
@override
String get gettingToken => '正在獲取Token...';
@override
String get goBackQ => '返回?';
@override
String get goto => '前往';

View File

@@ -60,7 +60,7 @@ SPEC CHECKSUMS:
file_picker: 1d63c4949e05e386da864365f8c13e1e64787675
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028

View File

@@ -470,7 +470,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -478,7 +478,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -602,7 +602,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -610,7 +610,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -628,7 +628,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -636,7 +636,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -657,7 +657,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -670,7 +670,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -696,7 +696,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -709,7 +709,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -732,7 +732,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -745,7 +745,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -28,8 +28,7 @@ class MyApp extends StatelessWidget {
// Issue #57
// if not [ok] -> [AMOLED] mode, use [ThemeMode.dark]
final themeMode = isAMOLED ? ThemeMode.values[tMode] : ThemeMode.dark;
final localeStr = _setting.locale.fetch();
final locale = localeStr?.toLocale;
final locale = _setting.locale.fetch()?.toLocale;
final darkTheme = ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
@@ -47,39 +46,25 @@ class MyApp extends StatelessWidget {
useMaterial3: true,
colorSchemeSeed: primaryColor,
),
darkTheme: isAMOLED
? darkTheme
: darkTheme.copyWith(
scaffoldBackgroundColor: Colors.black,
dialogBackgroundColor: Colors.black,
drawerTheme: const DrawerThemeData(
backgroundColor: Colors.black,
),
appBarTheme: const AppBarTheme(
backgroundColor: Colors.black,
),
dialogTheme: const DialogTheme(
backgroundColor: Colors.black,
),
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: Colors.black,
),
listTileTheme: const ListTileThemeData(
tileColor: Colors.black12,
),
cardTheme: const CardTheme(
color: Colors.black12,
),
navigationBarTheme: const NavigationBarThemeData(
backgroundColor: Colors.black,
),
popupMenuTheme: const PopupMenuThemeData(
color: Colors.black,
),
),
darkTheme: isAMOLED ? darkTheme : _getAmoledTheme(darkTheme),
home: fullScreen ? const FullScreenPage() : const HomePage(),
);
},
);
}
}
ThemeData _getAmoledTheme(ThemeData darkTheme) => darkTheme.copyWith(
scaffoldBackgroundColor: Colors.black,
dialogBackgroundColor: Colors.black,
drawerTheme: const DrawerThemeData(backgroundColor: Colors.black),
appBarTheme: const AppBarTheme(backgroundColor: Colors.black),
dialogTheme: const DialogTheme(backgroundColor: Colors.black),
bottomSheetTheme:
const BottomSheetThemeData(backgroundColor: Colors.black),
listTileTheme: const ListTileThemeData(tileColor: Colors.black12),
cardTheme: const CardTheme(color: Colors.black12),
navigationBarTheme:
const NavigationBarThemeData(backgroundColor: Colors.black),
popupMenuTheme: const PopupMenuThemeData(color: Colors.black),
);

View File

@@ -80,7 +80,8 @@ List<OneTimeCpuStatus> parseCPU(String raw) {
if (item == '') break;
final id = item.split(' ').first;
final matches = item.replaceFirst(id, '').trim().split(' ');
cpus.add(OneTimeCpuStatus(
cpus.add(
OneTimeCpuStatus(
id,
int.parse(matches[0]),
int.parse(matches[1]),

View File

@@ -2,8 +2,8 @@
class BuildData {
static const String name = "ServerBox";
static const int build = 476;
static const int build = 477;
static const String engine = "3.10.6";
static const String buildAt = "2023-08-17 18:45:06.973608";
static const int modifications = 5;
static const String buildAt = "2023-08-17 21:39:55.328086";
static const int modifications = 14;
}

View File

@@ -78,6 +78,7 @@
"fullScreenJitterHelp": "Einbrennen des Bildschirms verhindern",
"getPushTokenFailed": "Push-Token kann nicht abgerufen werden",
"gettingToken": "Getting token...",
"goBackQ": "Zurückkommen?",
"goto": "Pfad öffnen",
"homeWidgetUrlConfig": "Home-Widget-Link konfigurieren",
"host": "Host",

View File

@@ -78,6 +78,7 @@
"fullScreenJitterHelp": "To avoid screen burn-in",
"getPushTokenFailed": "Can't fetch push token",
"gettingToken": "Getting token...",
"goBackQ": "Go back?",
"goto": "Go to",
"homeWidgetUrlConfig": "Config home widget url",
"host": "Host",

View File

@@ -78,6 +78,7 @@
"fullScreenJitterHelp": "Untuk menghindari pembakaran layar",
"getPushTokenFailed": "Tidak bisa mengambil token dorong",
"gettingToken": "Mendapatkan token ...",
"goBackQ": "Datang kembali?",
"goto": "Pergi ke",
"homeWidgetUrlConfig": "Konfigurasi URL Widget Rumah",
"host": "Host",

View File

@@ -78,6 +78,7 @@
"fullScreenJitterHelp": "防止烧屏",
"getPushTokenFailed": "未能获取到推送token",
"gettingToken": "正在获取Token...",
"goBackQ": "返回?",
"goto": "前往",
"homeWidgetUrlConfig": "桌面部件链接配置",
"host": "主机",

View File

@@ -78,6 +78,7 @@
"fullScreenJitterHelp": "防止燒屏",
"getPushTokenFailed": "未能獲取到推送token",
"gettingToken": "正在獲取Token...",
"goBackQ": "返回?",
"goto": "前往",
"homeWidgetUrlConfig": "桌面部件鏈接配置",
"host": "主機",

View File

@@ -18,11 +18,11 @@ import 'data/store/snippet.dart';
GetIt locator = GetIt.instance;
void setupLocatorForServices() {
void _setupLocatorForServices() {
locator.registerLazySingleton(() => AppService());
}
void setupLocatorForProviders() {
void _setupLocatorForProviders() {
locator.registerSingleton(AppProvider());
locator.registerSingleton(PkgProvider());
locator.registerSingleton(DebugProvider());
@@ -34,7 +34,7 @@ void setupLocatorForProviders() {
locator.registerSingleton(SftpProvider());
}
Future<void> setupLocatorForStores() async {
Future<void> _setupLocatorForStores() async {
final setting = SettingStore();
await setting.init(boxName: 'setting');
locator.registerSingleton(setting);
@@ -57,7 +57,7 @@ Future<void> setupLocatorForStores() async {
}
Future<void> setupLocator() async {
await setupLocatorForStores();
setupLocatorForProviders();
setupLocatorForServices();
await _setupLocatorForStores();
_setupLocatorForProviders();
_setupLocatorForServices();
}

View File

@@ -3,17 +3,20 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:logging/logging.dart';
import 'package:macos_window_utils/window_manipulator.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:toolbox/data/model/app/net_view.dart';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
import 'package:toolbox/view/widget/custom_appbar.dart';
import 'app.dart';
import 'core/analysis.dart';
import 'core/utils/platform.dart';
import 'core/utils/ui.dart';
import 'data/model/app/net_view.dart';
import 'data/model/server/private_key_info.dart';
import 'data/model/server/server_private_info.dart';
import 'data/model/server/snippet.dart';
import 'data/model/ssh/virtual_key.dart';
import 'data/provider/app.dart';
import 'data/provider/debug.dart';
import 'data/provider/docker.dart';
@@ -27,69 +30,10 @@ import 'data/store/setting.dart';
import 'locator.dart';
import 'view/widget/rebuild.dart';
late final DebugProvider _debug;
Future<void> initApp() async {
await initHive();
await setupLocator();
_debug = locator<DebugProvider>();
locator<SnippetProvider>().loadData();
locator<PrivateKeyProvider>().loadData();
final settings = locator<SettingStore>();
await loadFontFile(settings.fontPath.fetch());
SharedPreferences.setPrefix('');
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
var str = '[${record.loggerName}][${record.level.name}]: ${record.message}';
if (record.error != null) {
str += '\n${record.error}';
_debug.addMultiline(record.error.toString(), Colors.red);
}
if (record.stackTrace != null) {
str += '\n${record.stackTrace}';
_debug.addMultiline(record.stackTrace.toString(), Colors.white);
}
// ignore: avoid_print
print(str);
});
}
Future<void> initHive() async {
await Hive.initFlutter();
// 以 typeId 为顺序
Hive.registerAdapter(PrivateKeyInfoAdapter());
Hive.registerAdapter(SnippetAdapter());
Hive.registerAdapter(ServerPrivateInfoAdapter());
Hive.registerAdapter(VirtKeyAdapter());
Hive.registerAdapter(NetViewTypeAdapter());
}
void runInZone(void Function() body) {
final zoneSpec = ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, line);
// This is a hack to avoid
// `setState() or markNeedsBuild() called during build`
// error.
Future.delayed(const Duration(milliseconds: 1), () {
_debug.addText(line);
});
},
);
runZonedGuarded(
body,
(obj, trace) => Analysis.recordException(trace),
zoneSpecification: zoneSpec,
);
}
DebugProvider? _debug;
Future<void> main() async {
runInZone(() async {
_runInZone(() async {
await initApp();
runApp(
MultiProvider(
@@ -111,3 +55,86 @@ Future<void> main() async {
);
});
}
void _runInZone(void Function() body) {
final zoneSpec = ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
parent.print(zone, line);
// This is a hack to avoid
// `setState() or markNeedsBuild() called during build`
// error.
Future.delayed(const Duration(milliseconds: 1), () {
_debug?.addText(line);
});
},
);
runZonedGuarded(
body,
(obj, trace) => Analysis.recordException(trace),
zoneSpecification: zoneSpec,
);
}
Future<void> initApp() async {
await _initMacOSWindow();
// Base of all data.
await _initHive();
await setupLocator();
// Setup [DebugProvider] first to catch all logs.
_debug = locator<DebugProvider>();
_setupLogger();
_setupProviders();
// Load font
final settings = locator<SettingStore>();
loadFontFile(settings.fontPath.fetch());
// SharedPreferences is only used on Android for saving home widgets settings.
if (!isAndroid) return;
SharedPreferences.setPrefix('');
}
void _setupProviders() {
locator<SnippetProvider>().loadData();
locator<PrivateKeyProvider>().loadData();
}
Future<void> _initHive() async {
await Hive.initFlutter();
// 以 typeId 为顺序
Hive.registerAdapter(PrivateKeyInfoAdapter());
Hive.registerAdapter(SnippetAdapter());
Hive.registerAdapter(ServerPrivateInfoAdapter());
Hive.registerAdapter(VirtKeyAdapter());
Hive.registerAdapter(NetViewTypeAdapter());
}
void _setupLogger() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
var str = '[${record.loggerName}][${record.level.name}]: ${record.message}';
if (record.error != null) {
str += '\n${record.error}';
_debug?.addMultiline(record.error.toString(), Colors.red);
}
if (record.stackTrace != null) {
str += '\n${record.stackTrace}';
_debug?.addMultiline(record.stackTrace.toString(), Colors.white);
}
// ignore: avoid_print
print(str);
});
}
Future<void> _initMacOSWindow() async {
if (!isMacOS) return;
WidgetsFlutterBinding.ensureInitialized();
await WindowManipulator.initialize();
WindowManipulator.makeTitlebarTransparent();
WindowManipulator.enableFullSizeContentView();
WindowManipulator.hideTitle();
await CustomAppBar.updateTitlebarHeight();
}

View File

@@ -19,6 +19,7 @@ import '../../data/store/private_key.dart';
import '../../data/store/server.dart';
import '../../data/store/snippet.dart';
import '../../locator.dart';
import '../widget/custom_appbar.dart';
const backupFormatVersion = 1;
@@ -34,7 +35,7 @@ class BackupPage extends StatelessWidget {
Widget build(BuildContext context) {
final s = S.of(context)!;
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(s.backupAndRestore, style: textSize18),
),
body: _buildBody(context, s),

View File

@@ -7,6 +7,7 @@ import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/widget/value_notifier.dart';
import '../../core/utils/ui.dart';
import '../widget/custom_appbar.dart';
import '../widget/input_field.dart';
import '../widget/popup_menu.dart';
import '../widget/round_rect_card.dart';
@@ -52,7 +53,7 @@ class _ConvertPageState extends State<ConvertPage>
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(_s.convert),
),
body: SingleChildScrollView(

View File

@@ -3,6 +3,8 @@ import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/data/provider/debug.dart';
import '../widget/custom_appbar.dart';
class DebugPage extends StatefulWidget {
const DebugPage({Key? key}) : super(key: key);
@@ -14,7 +16,7 @@ class _DebugPageState extends State<DebugPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
leading: IconButton(
onPressed: () => context.pop(),
icon: const Icon(Icons.arrow_back, color: Colors.white),

View File

@@ -18,6 +18,7 @@ import '../../data/res/ui.dart';
import '../../data/res/url.dart';
import '../../data/store/docker.dart';
import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/popup_menu.dart';
import '../widget/round_rect_card.dart';
import '../widget/two_line_text.dart';
@@ -64,7 +65,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
Widget build(BuildContext context) {
return Consumer<DockerProvider>(builder: (_, ___, __) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
centerTitle: true,
title: TwoLineText(up: 'Docker', down: widget.spi.name),
actions: [

View File

@@ -15,6 +15,7 @@ import 'package:toolbox/data/res/highlight.dart';
import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/two_line_text.dart';
class EditorPage extends StatefulWidget {
@@ -68,55 +69,9 @@ class _EditorPageState extends State<EditorPage> with AfterLayoutMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: () {
if (_codeTheme != null) {
return _codeTheme!['root']!.backgroundColor;
}
return null;
}(),
appBar: AppBar(
centerTitle: true,
title: TwoLineText(up: getFileName(widget.path) ?? '', down: _s.editor),
actions: [
PopupMenuButton<String>(
icon: const Icon(Icons.language),
onSelected: (value) {
_controller.language = suffix2HighlightMap[value];
_langCode = value;
},
initialValue: _langCode,
itemBuilder: (BuildContext context) {
return suffix2HighlightMap.keys.map((e) {
return PopupMenuItem(
value: e,
child: Text(e),
);
}).toList();
},
)
],
),
body: Visibility(
visible: (_codeTheme != null),
replacement: const Center(
child: CircularProgressIndicator(),
),
child: SingleChildScrollView(
child: CodeTheme(
data: CodeThemeData(
styles: _codeTheme ??
(isDarkMode(context) ? monokaiTheme : a11yLightTheme)),
child: CodeField(
focusNode: _focusNode,
controller: _controller,
lineNumberStyle: const LineNumberStyle(
width: 47,
margin: 7,
),
),
),
),
),
backgroundColor: _codeTheme?['root']?.backgroundColor,
appBar: _buildAppBar(),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.done),
onPressed: () {
@@ -126,6 +81,54 @@ class _EditorPageState extends State<EditorPage> with AfterLayoutMixin {
);
}
PreferredSizeWidget _buildAppBar() {
return CustomAppBar(
title: TwoLineText(up: getFileName(widget.path) ?? '', down: _s.editor),
actions: [
PopupMenuButton<String>(
icon: const Icon(Icons.language),
onSelected: (value) {
_controller.language = suffix2HighlightMap[value];
_langCode = value;
},
initialValue: _langCode,
itemBuilder: (BuildContext context) {
return suffix2HighlightMap.keys.map((e) {
return PopupMenuItem(
value: e,
child: Text(e),
);
}).toList();
},
)
],
);
}
Widget _buildBody() {
return Visibility(
visible: _codeTheme != null,
replacement: const Center(
child: CircularProgressIndicator(),
),
child: SingleChildScrollView(
child: CodeTheme(
data: CodeThemeData(
styles: _codeTheme ??
(isDarkMode(context) ? monokaiTheme : a11yLightTheme)),
child: CodeField(
focusNode: _focusNode,
controller: _controller,
lineNumberStyle: const LineNumberStyle(
width: 47,
margin: 7,
),
),
),
),
);
}
@override
FutureOr<void> afterFirstLayout(BuildContext context) async {
if (widget.path != null) {

View File

@@ -20,6 +20,7 @@ import '../../data/res/ui.dart';
import '../../data/res/url.dart';
import '../../data/store/setting.dart';
import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/url_text.dart';
import 'backup.dart';
import 'convert.dart';
@@ -109,31 +110,10 @@ class _HomePageState extends State<HomePage>
@override
Widget build(BuildContext context) {
super.build(context);
final actions = <Widget>[
IconButton(
icon: const Icon(Icons.developer_mode, size: 23),
tooltip: _s.debug,
onPressed: () => AppRoute(
const DebugPage(),
'Debug Page',
).go(context),
),
];
if (isDesktop && _selectIndex.value == AppTab.server.index) {
actions.add(
IconButton(
icon: const Icon(Icons.refresh, size: 23),
tooltip: 'Refresh',
onPressed: () => _serverProvider.refreshData(),
),
);
}
return Scaffold(
drawer: _buildDrawer(),
appBar: AppBar(
title: const Text(BuildData.name),
actions: actions,
),
appBar: _buildAppBar(),
body: PageView.builder(
controller: _pageController,
itemCount: AppTab.values.length,
@@ -151,9 +131,44 @@ class _HomePageState extends State<HomePage>
);
}
PreferredSizeWidget _buildAppBar() {
final actions = <Widget>[
IconButton(
icon: const Icon(Icons.developer_mode, size: 23),
tooltip: _s.debug,
onPressed: () => AppRoute(
const DebugPage(),
'Debug Page',
).go(context),
),
];
if (isDesktop && _selectIndex.value == AppTab.server.index) {
actions.add(
ValueBuilder(
listenable: _selectIndex,
build: () {
if (_selectIndex.value != AppTab.server.index) {
return const SizedBox();
}
return IconButton(
icon: const Icon(Icons.refresh, size: 23),
tooltip: 'Refresh',
onPressed: () => _serverProvider.refreshData(),
);
},
),
);
}
return CustomAppBar(
title: const Text(BuildData.name),
actions: actions,
);
}
Widget _buildBottomBar() {
return NavigationBar(
selectedIndex: _selectIndex.value,
height: kBottomNavigationBarHeight * 1.2,
animationDuration: const Duration(milliseconds: 250),
onDestinationSelected: (int index) {
if (_selectIndex.value == index) return;

View File

@@ -12,6 +12,7 @@ import '../../data/provider/pkg.dart';
import '../../data/provider/server.dart';
import '../../data/res/ui.dart';
import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/round_rect_card.dart';
import '../widget/two_line_text.dart';
@@ -72,7 +73,7 @@ class _PkgManagePageState extends State<PkgManagePage>
Widget build(BuildContext context) {
return Consumer<PkgProvider>(builder: (_, pkg, __) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
centerTitle: true,
title: TwoLineText(up: _s.pkg, down: widget.spi.name),
),

View File

@@ -17,6 +17,7 @@ import '../../../data/model/server/private_key_info.dart';
import '../../../data/provider/private_key.dart';
import '../../../data/res/ui.dart';
import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
const _format = 'text/plain';
@@ -89,7 +90,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
},
icon: const Icon(Icons.delete))
];
return AppBar(
return CustomAppBar(
title: Text(_s.edit, style: textSize18),
actions: actions,
);

View File

@@ -14,6 +14,7 @@ import '../../../core/utils/platform.dart';
import '../../../data/model/server/private_key_info.dart';
import '../../../data/provider/private_key.dart';
import '../../../data/res/ui.dart';
import '../../widget/custom_appbar.dart';
import 'edit.dart';
import '../../../view/widget/round_rect_card.dart';
@@ -37,7 +38,7 @@ class _PrivateKeyListState extends State<PrivateKeysListPage>
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(_s.privateKey, style: textSize18),
),
body: _buildBody(),

View File

@@ -15,6 +15,7 @@ import 'package:toolbox/view/widget/two_line_text.dart';
import '../../data/provider/server.dart';
import '../../locator.dart';
import '../widget/custom_appbar.dart';
class ProcessPage extends StatefulWidget {
final ServerPrivateInfo spi;
@@ -124,7 +125,7 @@ class _ProcessPageState extends State<ProcessPage> {
);
}
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
centerTitle: true,
title: TwoLineText(up: widget.spi.name, down: _s.process),
actions: actions,

View File

@@ -14,6 +14,7 @@ import '../../../data/res/default.dart';
import '../../../data/res/ui.dart';
import '../../../data/store/setting.dart';
import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/round_rect_card.dart';
class ServerDetailPage extends StatefulWidget {
@@ -75,7 +76,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
Widget _buildMainPage(Server si) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(si.spi.name, style: textSize18),
),
body: ReorderableListView.builder(

View File

@@ -15,6 +15,7 @@ import '../../../data/provider/server.dart';
import '../../../data/res/ui.dart';
import '../../../data/store/private_key.dart';
import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/tag/editor.dart';
import '../private_key/edit.dart';
@@ -116,7 +117,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
icon: const Icon(Icons.delete),
);
final actions = widget.spi != null ? [delBtn] : null;
return AppBar(
return CustomAppBar(
title: Text(_s.edit, style: textSize18),
actions: actions,
);

View File

@@ -29,6 +29,7 @@ import '../../data/res/ui.dart';
import '../../data/store/server.dart';
import '../../data/store/setting.dart';
import '../../locator.dart';
import '../widget/custom_appbar.dart';
import '../widget/future_widget.dart';
import '../widget/round_rect_card.dart';
@@ -100,7 +101,7 @@ class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(_s.setting),
),
body: ListView(

View File

@@ -9,6 +9,7 @@ import '../../../data/model/server/snippet.dart';
import '../../../data/provider/snippet.dart';
import '../../../data/res/ui.dart';
import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/tag/editor.dart';
class SnippetEditPage extends StatefulWidget {
@@ -54,7 +55,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(_s.edit, style: textSize18),
actions: _buildAppBarActions(),
),

View File

@@ -8,7 +8,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/extension/uint8list.dart';
import 'package:xterm/xterm.dart';
import '../../../core/route.dart';
@@ -341,19 +340,7 @@ class _SSHPageState extends State<SSHPage> {
),
);
_discontinuityTimer = Timer.periodic(
const Duration(seconds: 5),
(_) async {
var throwTimeout = true;
Future.delayed(const Duration(seconds: 3), () {
if (throwTimeout) {
_catchTimeout();
}
});
await _client?.run('echo 1').string;
throwTimeout = false;
},
);
_setupDiscontinuityTimer();
if (_session == null) {
showSnackBar(context, const Text('Null session'));
@@ -394,14 +381,30 @@ class _SSHPageState extends State<SSHPage> {
.listen(_terminal.write);
}
void _setupDiscontinuityTimer() {
_discontinuityTimer = Timer.periodic(
const Duration(seconds: 5),
(_) async {
var throwTimeout = true;
Future.delayed(const Duration(seconds: 3), () {
if (throwTimeout) {
_catchTimeout();
}
});
await _client?.ping();
throwTimeout = false;
},
);
}
void _catchTimeout() {
_discontinuityTimer?.cancel();
if (!mounted) return;
_write('\n\nConnection lost\r\n');
showRoundDialog(
context: context,
title: Text(_s.disconnected),
child: Text('Go back?'),
title: Text(_s.attention),
child: Text('${_s.disconnected}\n${_s.goBackQ}'),
barrierDismiss: false,
actions: [
TextButton(

View File

@@ -9,6 +9,8 @@ import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
import '../../widget/custom_appbar.dart';
class SSHVirtKeySettingPage extends StatefulWidget {
const SSHVirtKeySettingPage({Key? key}) : super(key: key);
@@ -29,7 +31,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
title: Text(_s.editVirtKeys),
),
body: _buildBody(),

View File

@@ -22,6 +22,7 @@ import '../../../core/utils/ui.dart';
import '../../../data/model/app/path_with_prefix.dart';
import '../../../data/res/path.dart';
import '../../../data/res/ui.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/fade_in.dart';
import 'sftp_mission.dart';
@@ -64,7 +65,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
leading: IconButton(
icon: const BackButtonIcon(),
onPressed: () {

View File

@@ -26,6 +26,7 @@ import '../../../data/provider/sftp.dart';
import '../../../data/res/path.dart';
import '../../../data/res/ui.dart';
import '../../../locator.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/fade_in.dart';
import '../../widget/input_field.dart';
import '../../widget/two_line_text.dart';
@@ -74,7 +75,7 @@ class _SftpPageState extends State<SftpPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
appBar: CustomAppBar(
leading: IconButton(
icon: const BackButtonIcon(),
onPressed: () {

View File

@@ -13,6 +13,7 @@ import '../../../core/utils/ui.dart';
import '../../../data/model/sftp/req.dart';
import '../../../data/provider/sftp.dart';
import '../../../data/res/ui.dart';
import '../../widget/custom_appbar.dart';
import '../../widget/round_rect_card.dart';
class SftpMissionPage extends StatefulWidget {
@@ -34,11 +35,8 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
_s.mission,
style: textSize18,
),
appBar: CustomAppBar(
title: Text(_s.mission, style: textSize18),
),
body: _buildBody(),
);

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:macos_window_utils/window_manipulator.dart';
double? _titlebarHeight;
class CustomAppBar extends AppBar implements PreferredSizeWidget {
CustomAppBar({
super.key,
super.title,
super.actions,
super.centerTitle,
super.leading,
super.backgroundColor,
}) : super(toolbarHeight: (_titlebarHeight ?? 0) + kToolbarHeight);
static Future<void> updateTitlebarHeight() async {
final newTitlebarHeight = await WindowManipulator.getTitlebarHeight();
if (_titlebarHeight != newTitlebarHeight) {
_titlebarHeight = newTitlebarHeight;
}
}
}

View File

@@ -5,12 +5,14 @@
import FlutterMacOS
import Foundation
import macos_window_utils
import path_provider_foundation
import share_plus
import shared_preferences_foundation
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))

View File

@@ -1,4 +1,4 @@
platform :osx, '10.14'
platform :osx, '10.15'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -1,5 +1,7 @@
PODS:
- FlutterMacOS (1.0.0)
- macos_window_utils (1.0.0):
- FlutterMacOS
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
@@ -13,6 +15,7 @@ PODS:
DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral`)
- macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
@@ -21,6 +24,8 @@ DEPENDENCIES:
EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral
macos_window_utils:
:path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
share_plus:
@@ -32,11 +37,12 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3
COCOAPODS: 1.12.1

View File

@@ -195,7 +195,6 @@
15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */,
7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
@@ -475,9 +474,9 @@
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -490,9 +489,9 @@
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -505,9 +504,9 @@
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 476;
CURRENT_PROJECT_VERSION = 477;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.476;
MARKETING_VERSION = 1.0.477;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -575,6 +574,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
@@ -701,6 +701,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -721,6 +722,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};

View File

@@ -510,6 +510,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
macos_window_utils:
dependency: "direct main"
description:
name: macos_window_utils
sha256: "43a90473f8786f00f07203e6819dab67e032f8896dafa4a6f85fbc71fba32c0b"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
matcher:
dependency: transitive
description:

View File

@@ -45,6 +45,7 @@ dependencies:
code_text_field: ^1.1.0
shared_preferences: ^2.1.1
crypto: ^3.0.3
macos_window_utils: ^1.2.0
dev_dependencies:
flutter_native_splash: ^2.1.6