From 8004c410948f1488c341b7500318b073f00df668 Mon Sep 17 00:00:00 2001 From: lollipopkit Date: Wed, 30 Aug 2023 15:41:41 +0800 Subject: [PATCH] #146 new: option of server tab old ui --- lib/data/model/app/menu.dart | 17 ++ lib/data/store/setting.dart | 7 + lib/view/page/server/tab.dart | 16 +- lib/view/page/storage/sftp.dart | 11 +- lib/view/widget/server_func_btns.dart | 275 +++++++++++++++----------- 5 files changed, 198 insertions(+), 128 deletions(-) diff --git a/lib/data/model/app/menu.dart b/lib/data/model/app/menu.dart index e045a23f..a300705e 100644 --- a/lib/data/model/app/menu.dart +++ b/lib/data/model/app/menu.dart @@ -26,6 +26,23 @@ enum ServerTabMenuType { return Icons.terminal; } } + + String text(S s) { + switch (this) { + case ServerTabMenuType.sftp: + return 'SFTP'; + case ServerTabMenuType.snippet: + return s.snippet; + case ServerTabMenuType.pkg: + return s.pkg; + case ServerTabMenuType.docker: + return 'Docker'; + case ServerTabMenuType.process: + return s.process; + case ServerTabMenuType.terminal: + return s.terminal; + } + } } enum DockerMenuType { diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index a11e1488..8f702623 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -146,4 +146,11 @@ class SettingStore extends PersistentStore { 'sftpRmrfDir', true, ); + + /// Discussion #146 + late final serverTabUseOldUI = StoreProperty( + box, + 'serverTabUseOldUI', + false, + ); } diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index 0e3efa66..f362b812 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -238,7 +238,9 @@ class _ServerPageState extends State ), ), height13, - if (_settingStore.moveOutServerTabFuncBtns.fetch()) + if (_settingStore.moveOutServerTabFuncBtns.fetch() && + // Discussion #146 + !_settingStore.serverTabUseOldUI.fetch()) SizedBox( height: 27, child: ServerFuncBtns(spi: spi, s: _s), @@ -283,7 +285,13 @@ class _ServerPageState extends State ) ], ), - _buildTopRightText(ss, cs), + Row( + children: [ + _buildTopRightText(ss, cs), + if (_settingStore.serverTabUseOldUI.fetch()) + ServerFuncBtnsTopRight(spi: spi, s: _s) + ], + ) ], ), ); @@ -446,7 +454,9 @@ class _ServerPageState extends State if (cs != ServerState.finished) { return 23.0; } - if (_settingStore.moveOutServerTabFuncBtns.fetch()) { + if (_settingStore.moveOutServerTabFuncBtns.fetch() && + // Discussion #146 + !_settingStore.serverTabUseOldUI.fetch()) { return 132; } return 107; diff --git a/lib/view/page/storage/sftp.dart b/lib/view/page/storage/sftp.dart index 98e03249..37c5f331 100644 --- a/lib/view/page/storage/sftp.dart +++ b/lib/view/page/storage/sftp.dart @@ -345,11 +345,12 @@ class _SftpPageState extends State with AfterLayoutMixin { onTap: () => _download(context, file), ), // Only show decompress option when the file is a compressed file - if (_canDecompress(file.filename)) ListTile( - leading: const Icon(Icons.folder_zip), - title: Text(_s.decompress), - onTap: () => _decompress(context, file), - ), + if (_canDecompress(file.filename)) + ListTile( + leading: const Icon(Icons.folder_zip), + title: Text(_s.decompress), + onTap: () => _decompress(context, file), + ), ]); } showRoundDialog( diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index 7932f170..bda49855 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -14,8 +14,42 @@ import '../../data/model/server/server_private_info.dart'; import '../../data/model/server/snippet.dart'; import '../../data/provider/snippet.dart'; import '../../locator.dart'; +import 'popup_menu.dart'; import 'tag.dart'; +class ServerFuncBtnsTopRight extends StatelessWidget { + final ServerPrivateInfo spi; + final S s; + + const ServerFuncBtnsTopRight({ + super.key, + required this.spi, + required this.s, + }); + + @override + Widget build(BuildContext context) { + return PopupMenu( + items: ServerTabMenuType.values + .map((e) => PopupMenuItem( + value: e, + child: Row( + children: [ + Icon(e.icon), + const SizedBox( + width: 10, + ), + Text(e.text(s)), + ], + ), + )) + .toList(), + padding: const EdgeInsets.symmetric(horizontal: 10), + onSelected: (val) => _onTapMoreBtns(val, spi, context, s), + ); + } +} + class ServerFuncBtns extends StatelessWidget { const ServerFuncBtns({ super.key, @@ -28,77 +62,13 @@ class ServerFuncBtns extends StatelessWidget { final S s; final double? iconSize; - void _onTapMoreBtns( - ServerTabMenuType value, - ServerPrivateInfo spi, - BuildContext context, - ) async { - switch (value) { - case ServerTabMenuType.pkg: - AppRoute.pkg(spi: spi).checkGo( - context: context, - check: () => _checkClient(context, spi.id), - ); - break; - case ServerTabMenuType.sftp: - AppRoute.sftp(spi: spi).checkGo( - context: context, - check: () => _checkClient(context, spi.id), - ); - break; - case ServerTabMenuType.snippet: - final provider = locator(); - final snippets = await showDialog>( - context: context, - builder: (_) => TagPicker( - items: provider.snippets, - tags: provider.tags.toSet(), - ), - ); - if (snippets == null) { - return; - } - final result = - await locator().runSnippets(spi.id, snippets); - if (result != null && result.isNotEmpty) { - showRoundDialog( - context: context, - title: Text(s.result), - child: Text(result), - actions: [ - TextButton( - onPressed: () => copy2Clipboard(result), - child: Text(s.copy), - ) - ], - ); - } - break; - case ServerTabMenuType.docker: - AppRoute.docker(spi: spi).checkGo( - context: context, - check: () => _checkClient(context, spi.id), - ); - break; - case ServerTabMenuType.process: - AppRoute.process(spi: spi).checkGo( - context: context, - check: () => _checkClient(context, spi.id), - ); - break; - case ServerTabMenuType.terminal: - _gotoSSH(spi, context); - break; - } - } - @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: ServerTabMenuType.values .map((e) => IconButton( - onPressed: () => _onTapMoreBtns(e, spi, context), + onPressed: () => _onTapMoreBtns(e, spi, context, s), padding: EdgeInsets.zero, tooltip: e.name, icon: Icon(e.icon, size: iconSize ?? 15), @@ -106,62 +76,127 @@ class ServerFuncBtns extends StatelessWidget { .toList(), ); } +} - Future _gotoSSH( - ServerPrivateInfo spi, - BuildContext context, - ) async { - // as a `Mobile first` app -> handle mobile first - // - // run built-in ssh on macOS due to incompatibility - if (!isDesktop || isMacOS) { - AppRoute.ssh(spi: spi).go(context); - return; - } - final extraArgs = []; - if (spi.port != 22) { - extraArgs.addAll(['-p', '${spi.port}']); - } - - final path = () { - final tempKeyFileName = 'srvbox_pk_${spi.pubKeyId}'; - return pathJoin(Directory.systemTemp.path, tempKeyFileName); - }(); - final file = File(path); - final shouldGenKey = spi.pubKeyId != null; - if (shouldGenKey) { - if (await file.exists()) { - await file.delete(); +void _onTapMoreBtns( + ServerTabMenuType value, + ServerPrivateInfo spi, + BuildContext context, + S s, +) async { + switch (value) { + case ServerTabMenuType.pkg: + AppRoute.pkg(spi: spi).checkGo( + context: context, + check: () => _checkClient(context, spi.id, s.waitConnection), + ); + break; + case ServerTabMenuType.sftp: + AppRoute.sftp(spi: spi).checkGo( + context: context, + check: () => _checkClient(context, spi.id, s.waitConnection), + ); + break; + case ServerTabMenuType.snippet: + final provider = locator(); + final snippets = await showDialog>( + context: context, + builder: (_) => TagPicker( + items: provider.snippets, + tags: provider.tags.toSet(), + ), + ); + if (snippets == null) { + return; } - await file.writeAsString(getPrivateKey(spi.pubKeyId!)); - extraArgs.addAll(["-i", path]); - } - - List sshCommand = ["ssh", "${spi.user}@${spi.ip}"] + extraArgs; - final system = Platform.operatingSystem; - switch (system) { - case "windows": - await Process.start("cmd", ["/c", "start"] + sshCommand); - break; - case "linux": - await Process.start("x-terminal-emulator", ["-e"] + sshCommand); - break; - default: - showSnackBar(context, Text('Mismatch system: $system')); - } - // For security reason, delete the private key file after use - if (shouldGenKey) { - if (!await file.exists()) return; - await Future.delayed(const Duration(seconds: 2), file.delete); - } - } - - bool _checkClient(BuildContext context, String id) { - final server = locator().servers[id]; - if (server == null || server.client == null) { - showSnackBar(context, Text(s.waitConnection)); - return false; - } - return true; + final result = + await locator().runSnippets(spi.id, snippets); + if (result != null && result.isNotEmpty) { + showRoundDialog( + context: context, + title: Text(s.result), + child: Text(result), + actions: [ + TextButton( + onPressed: () => copy2Clipboard(result), + child: Text(s.copy), + ) + ], + ); + } + break; + case ServerTabMenuType.docker: + AppRoute.docker(spi: spi).checkGo( + context: context, + check: () => _checkClient(context, spi.id, s.waitConnection), + ); + break; + case ServerTabMenuType.process: + AppRoute.process(spi: spi).checkGo( + context: context, + check: () => _checkClient(context, spi.id, s.waitConnection), + ); + break; + case ServerTabMenuType.terminal: + _gotoSSH(spi, context); + break; } } + +Future _gotoSSH( + ServerPrivateInfo spi, + BuildContext context, +) async { + // as a `Mobile first` app -> handle mobile first + // + // run built-in ssh on macOS due to incompatibility + if (!isDesktop || isMacOS) { + AppRoute.ssh(spi: spi).go(context); + return; + } + final extraArgs = []; + if (spi.port != 22) { + extraArgs.addAll(['-p', '${spi.port}']); + } + + final path = () { + final tempKeyFileName = 'srvbox_pk_${spi.pubKeyId}'; + return pathJoin(Directory.systemTemp.path, tempKeyFileName); + }(); + final file = File(path); + final shouldGenKey = spi.pubKeyId != null; + if (shouldGenKey) { + if (await file.exists()) { + await file.delete(); + } + await file.writeAsString(getPrivateKey(spi.pubKeyId!)); + extraArgs.addAll(["-i", path]); + } + + List sshCommand = ["ssh", "${spi.user}@${spi.ip}"] + extraArgs; + final system = Platform.operatingSystem; + switch (system) { + case "windows": + await Process.start("cmd", ["/c", "start"] + sshCommand); + break; + case "linux": + await Process.start("x-terminal-emulator", ["-e"] + sshCommand); + break; + default: + showSnackBar(context, Text('Mismatch system: $system')); + } + // For security reason, delete the private key file after use + if (shouldGenKey) { + if (!await file.exists()) return; + await Future.delayed(const Duration(seconds: 2), file.delete); + } +} + +bool _checkClient(BuildContext context, String id, String msg) { + final server = locator().servers[id]; + if (server == null || server.client == null) { + showSnackBar(context, Text(msg)); + return false; + } + return true; +}