diff --git a/README.md b/README.md index 55f05512..7773d513 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ If ServerBox app has any bug, please open an [issue](https://github.com/lollipop
- + @@ -67,10 +67,10 @@ If ServerBox app has any bug, please open an [issue](https://github.com/lollipop - + - + diff --git a/README_zh.md b/README_zh.md index a75f158f..b64e67d5 100644 --- a/README_zh.md +++ b/README_zh.md @@ -48,7 +48,7 @@
- + @@ -67,10 +67,10 @@ - + - + diff --git a/imgs/detail.jpg b/imgs/detail.jpg index 0eb37305..82673614 100644 Binary files a/imgs/detail.jpg and b/imgs/detail.jpg differ diff --git a/imgs/docker.jpeg b/imgs/docker.jpeg new file mode 100644 index 00000000..a4028d4f Binary files /dev/null and b/imgs/docker.jpeg differ diff --git a/imgs/docker.jpg b/imgs/docker.jpg deleted file mode 100644 index 63cbe17c..00000000 Binary files a/imgs/docker.jpg and /dev/null differ diff --git a/imgs/server.jpeg b/imgs/server.jpeg new file mode 100644 index 00000000..bd570941 Binary files /dev/null and b/imgs/server.jpeg differ diff --git a/imgs/server.jpg b/imgs/server.jpg deleted file mode 100644 index b2727886..00000000 Binary files a/imgs/server.jpg and /dev/null differ diff --git a/imgs/sftp.jpeg b/imgs/sftp.jpeg new file mode 100644 index 00000000..773d7a82 Binary files /dev/null and b/imgs/sftp.jpeg differ diff --git a/imgs/sftp.jpg b/imgs/sftp.jpg deleted file mode 100644 index 340c0113..00000000 Binary files a/imgs/sftp.jpg and /dev/null differ diff --git a/lib/data/res/color.dart b/lib/data/res/color.dart index a7af2b1d..c9b1aea0 100644 --- a/lib/data/res/color.dart +++ b/lib/data/res/color.dart @@ -8,4 +8,4 @@ Color primaryColor = Color(locator().primaryColor.fetch()!); final contentColor = DynamicColor(Colors.black87, Colors.white70); final bgColor = DynamicColor(Colors.white, Colors.black); -final progressColor = DynamicColor(Colors.grey.shade100, Colors.white10); +final progressColor = DynamicColor(Colors.black12, Colors.white10); diff --git a/lib/data/res/ui.dart b/lib/data/res/ui.dart index ed113833..dde53279 100644 --- a/lib/data/res/ui.dart +++ b/lib/data/res/ui.dart @@ -24,6 +24,8 @@ const height13 = SizedBox(height: 13); const width13 = SizedBox(width: 13); const width7 = SizedBox(width: 7); +/// Misc + const popMenuChild = Padding( padding: EdgeInsets.only(left: 7), child: Icon( diff --git a/lib/view/page/convert.dart b/lib/view/page/convert.dart index 0ae73ba9..2ecc077d 100644 --- a/lib/view/page/convert.dart +++ b/lib/view/page/convert.dart @@ -5,7 +5,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../../core/utils/ui.dart'; -import '../../data/res/color.dart'; import '../widget/input_field.dart'; import '../widget/round_rect_card.dart'; @@ -21,7 +20,6 @@ class _ConvertPageState extends State late TextEditingController _textEditingController; late TextEditingController _textEditingControllerResult; late MediaQueryData _media; - late ThemeData _theme; late S _s; int _typeOptionIndex = 0; @@ -37,7 +35,6 @@ class _ConvertPageState extends State void didChangeDependencies() { super.didChangeDependencies(); _media = MediaQuery.of(context); - _theme = Theme.of(context); _s = S.of(context)!; } @@ -105,10 +102,14 @@ class _ConvertPageState extends State 'URL $encode', 'URL $decode' ]; + final items = typeOption + .map( + (e) => PopupMenuItem(value: typeOption.indexOf(e), child: Text(e)), + ) + .toList(); return RoundRectCard( - ExpansionTile( - tilePadding: const EdgeInsets.only(left: 7, right: 27), - childrenPadding: EdgeInsets.zero, + ListTile( + contentPadding: const EdgeInsets.only(right: 17), title: Row( children: [ TextButton( @@ -133,42 +134,30 @@ class _ConvertPageState extends State ), trailing: ConstrainedBox( constraints: BoxConstraints(maxWidth: _media.size.width * 0.35), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - typeOption[_typeOptionIndex], - textScaleFactor: 1.0, - textAlign: TextAlign.left, - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w500, - color: primaryColor, - ), - ), - Text( - _s.currentMode, - textScaleFactor: 1.0, - textAlign: TextAlign.right, - style: const TextStyle(fontSize: 9.0, color: Colors.grey), - ) - ], - ), - ), - children: typeOption - .map( - (e) => ListTile( - title: Text( - e, - style: TextStyle( - color: _theme.textTheme.bodyMedium?.color?.withAlpha(177), + child: buildPopuopMenu( + items: items, + onSelected: (p0) { + setState(() { + _typeOptionIndex = p0; + }); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + typeOption[_typeOptionIndex], + textScaleFactor: 1.0, + textAlign: TextAlign.right, + style: const TextStyle( + fontWeight: FontWeight.w500, + color: Colors.grey ), ), - trailing: _buildRadio(typeOption.indexOf(e)), - ), - ) - .toList(), + ], + ), + ), + ), ), ); } @@ -180,18 +169,6 @@ class _ConvertPageState extends State ); } - Radio _buildRadio(int index) { - return Radio( - value: index, - groupValue: _typeOptionIndex, - onChanged: (int? value) { - setState(() { - _typeOptionIndex = value ?? 0; - }); - }, - ); - } - @override bool get wantKeepAlive => true; } diff --git a/lib/view/page/docker.dart b/lib/view/page/docker.dart index 3551b587..5cdb682c 100644 --- a/lib/view/page/docker.dart +++ b/lib/view/page/docker.dart @@ -271,6 +271,7 @@ class _DockerManagePageState extends State { _buildPsItems(), _buildImages(), _buildEditHost(), + const SizedBox(height: 37), ].map((e) => RoundRectCard(e)).toList(), ); } @@ -279,59 +280,64 @@ class _DockerManagePageState extends State { if (_docker.images == null) { return const SizedBox(); } - return ExpansionTile( - title: Text(_s.imagesList), - subtitle: Text( - _s.dockerImagesFmt(_docker.images!.length), - style: grey, - ), - children: _docker.images! - .map( - (e) => ListTile( - title: Text(e.repo), - subtitle: Text('${e.tag} - ${e.size}'), - trailing: IconButton( - icon: const Icon(Icons.delete), - onPressed: () async { - showRoundDialog( - context: context, - child: Text(_s.sureDelete(e.repo)), - actions: [ - TextButton( - onPressed: () => context.pop(), - child: Text(_s.cancel), - ), - TextButton( - onPressed: () async { - context.pop(); - final result = await _docker.run( - 'docker rmi ${e.id} -f', + final items = _docker.images! + .map( + (e) => ListTile( + title: Text(e.repo), + subtitle: Text('${e.tag} - ${e.size}', style: grey), + trailing: IconButton( + padding: EdgeInsets.zero, + alignment: Alignment.centerRight, + icon: const Icon(Icons.delete), + onPressed: () async { + showRoundDialog( + context: context, + child: Text(_s.sureDelete(e.repo)), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text(_s.cancel), + ), + TextButton( + onPressed: () async { + context.pop(); + final result = await _docker.run( + 'docker rmi ${e.id} -f', + ); + if (result != null) { + showSnackBar( + context, + Text(getErrMsg(result) ?? _s.unknownError), ); - if (result != null) { - showSnackBar( - context, - Text(getErrMsg(result) ?? _s.unknownError), - ); - } - }, - child: Text( - _s.ok, - style: const TextStyle(color: Colors.red), - ), + } + }, + child: Text( + _s.ok, + style: const TextStyle(color: Colors.red), ), - ], - ); - }, - ), + ), + ], + ); + }, ), - ) - .toList(), + ), + ) + .toList(); + items.insert( + 0, + ListTile( + title: Text(_s.imagesList), + subtitle: Text( + _s.dockerImagesFmt(_docker.images!.length), + style: grey, + ), + ), ); + return Column(children: items); } Widget _buildLoading() { if (!_docker.isBusy) return const SizedBox(); - final haveLog = _docker.runLog != null; return Padding( padding: const EdgeInsets.all(17), child: Column( @@ -339,8 +345,8 @@ class _DockerManagePageState extends State { const Center( child: CircularProgressIndicator(), ), - haveLog ? const SizedBox(height: 17) : const SizedBox(), - haveLog ? Text(_docker.runLog!) : const SizedBox() + const SizedBox(height: 17), + Text(_docker.runLog ?? '...'), ], ), ); @@ -439,18 +445,25 @@ class _DockerManagePageState extends State { } Widget _buildPsItems() { - return ExpansionTile( - title: Text(_s.containerStatus), - subtitle: Text(_buildSubtitle(_docker.items!), style: grey), - children: _docker.items!.map( - (item) { - return ListTile( - title: Text(item.name), - subtitle: Text('${item.image} - ${item.status}'), - trailing: _buildMoreBtn(item, _docker.isBusy), - ); - }, - ).toList(), + final items = _docker.items!.map( + (item) { + return ListTile( + title: Text(item.name), + subtitle: Text('${item.image} - ${item.status}', + style: grey.copyWith(fontSize: 11)), + trailing: _buildMoreBtn(item, _docker.isBusy), + ); + }, + ).toList(); + items.insert( + 0, + ListTile( + title: Text(_s.containerStatus), + subtitle: Text(_buildSubtitle(_docker.items!), style: grey), + ), + ); + return Column( + children: items, ); } diff --git a/lib/view/page/home.dart b/lib/view/page/home.dart index da21ee85..cbe79338 100644 --- a/lib/view/page/home.dart +++ b/lib/view/page/home.dart @@ -185,7 +185,7 @@ class _MyHomePageState extends State height: MediaQuery.of(context).size.height * 0.07, ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 13), + padding: const EdgeInsets.symmetric(horizontal: 17), child: Column( children: [ ListTile( @@ -215,24 +215,6 @@ class _MyHomePageState extends State onTap: () => AppRoute(BackupPage(), 'backup page').go(context), ), - ListTile( - leading: const Icon(Icons.info), - title: Text(_s.feedback), - onTap: () => showRoundDialog( - context: context, - child: Text(_s.feedbackOnGithub), - actions: [ - TextButton( - onPressed: () => openUrl(issueUrl), - child: Text(_s.feedback), - ), - TextButton( - onPressed: () => context.pop(), - child: Text(_s.close), - ) - ], - ), - ), ListTile( leading: const Icon(Icons.snippet_folder), title: Text(_s.snippet), @@ -241,7 +223,7 @@ class _MyHomePageState extends State ), ListTile( leading: const Icon(Icons.text_snippet), - title: Text(_s.about), + title: Text('${_s.about} & ${_s.feedback}'), onTap: () { showRoundDialog( context: context, @@ -266,6 +248,10 @@ class _MyHomePageState extends State ], ), actions: [ + TextButton( + onPressed: () => openUrl(issueUrl), + child: Text(_s.feedback), + ), TextButton( onPressed: () => showLicensePage(context: context), child: Text(_s.license), diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 52565e95..8ea6cd51 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -240,7 +240,6 @@ class _ServerEditPageState extends State with AfterLayoutMixin { child: Text(_s.cancel), ) ], - barrierDismiss: false, ); if (cancel ?? true) { return; @@ -250,10 +249,10 @@ class _ServerEditPageState extends State with AfterLayoutMixin { showSnackBar(context, Text(_s.plzSelectKey)); return; } - if (_usernameController.text == '') { + if (_usernameController.text.isEmpty) { _usernameController.text = 'root'; } - if (_portController.text == '') { + if (_portController.text.isEmpty) { _portController.text = '22'; } diff --git a/lib/view/page/setting.dart b/lib/view/page/setting.dart index c12ba084..2ed524d4 100644 --- a/lib/view/page/setting.dart +++ b/lib/view/page/setting.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_material_color_picker/flutter_material_color_picker.dart'; import 'package:provider/provider.dart'; +import 'package:toolbox/core/utils/navigator.dart'; import 'package:toolbox/data/model/app/tab.dart'; import '../../core/utils/misc.dart'; @@ -427,6 +428,7 @@ class _SettingPageState extends State { TextButton( onPressed: () => setState(() { _setting.fontPath.delete(); + context.pop(); _showRestartSnackbar(); }), child: Text(_s.clear), @@ -452,6 +454,7 @@ class _SettingPageState extends State { _setting.fontPath.put(newPath); } + context.pop(); setState(() {}); _showRestartSnackbar(); return; diff --git a/lib/view/page/sftp/view.dart b/lib/view/page/sftp/view.dart index 1cbcea6b..3dbe3f35 100644 --- a/lib/view/page/sftp/view.dart +++ b/lib/view/page/sftp/view.dart @@ -36,7 +36,6 @@ class _SFTPPageState extends State { final SftpBrowserStatus _status = SftpBrowserStatus(); final ScrollController _scrollController = ScrollController(); - late MediaQueryData _media; late S _s; Server? _si; @@ -45,7 +44,6 @@ class _SFTPPageState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - _media = MediaQuery.of(context); _s = S.of(context)!; } @@ -121,43 +119,7 @@ class _SFTPPageState extends State { )), icon: const Icon(Icons.add), ), - IconButton( - padding: const EdgeInsets.all(0), - onPressed: () async { - final p = await showRoundDialog( - context: context, - title: Text(_s.goto), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - decoration: InputDecoration( - labelText: _s.path, - hintText: '/', - ), - onSubmitted: (value) => context.pop(value), - ), - ], - ), - actions: [ - TextButton( - onPressed: () => context.pop(), - child: Text(_s.cancel), - ) - ], - ); - - if (p != null) { - if (p.isEmpty) { - showSnackBar(context, Text(_s.fieldMustNotEmpty)); - return; - } - _status.path?.update(p); - listDir(path: p); - } - }, - icon: const Icon(Icons.gps_fixed), - ) + _buildGotoBtn(), ], ) ], @@ -165,30 +127,59 @@ class _SFTPPageState extends State { )); } - Widget get centerCircleLoading => Center( - child: Column( - children: [ - SizedBox( - height: _media.size.height * 0.4, - ), - const CircularProgressIndicator(), + Widget _buildGotoBtn() { + return IconButton( + padding: const EdgeInsets.all(0), + onPressed: () async { + final p = await showRoundDialog( + context: context, + title: Text(_s.goto), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: InputDecoration( + labelText: _s.path, + hintText: '/', + ), + onSubmitted: (value) => context.pop(value), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => context.pop(), + child: Text(_s.cancel), + ) ], - ), - ); + ); + + if (p != null) { + if (p.isEmpty) { + showSnackBar(context, Text(_s.fieldMustNotEmpty)); + return; + } + _status.path?.update(p); + listDir(path: p); + } + }, + icon: const Icon(Icons.gps_fixed), + ); + } Widget _buildFileView() { if (_client == null || _si?.state != ServerState.connected) { - return centerCircleLoading; + return centerLoading; } if (_status.isBusy) { - return centerCircleLoading; + return centerLoading; } if (_status.files == null) { _status.path = AbsolutePath('/'); listDir(path: '/', client: _client); - return centerCircleLoading; + return centerLoading; } else { return RefreshIndicator( child: FadeIn( diff --git a/lib/view/widget/input_field.dart b/lib/view/widget/input_field.dart index 279ec932..1a2c15f1 100644 --- a/lib/view/widget/input_field.dart +++ b/lib/view/widget/input_field.dart @@ -1,22 +1,27 @@ import 'package:flutter/material.dart'; import 'package:toolbox/view/widget/round_rect_card.dart'; -Widget buildInput(BuildContext context, TextEditingController controller, - {int maxLines = 20, - String? hint, - Function(String)? onSubmitted, - bool? obscureText}) { +Widget buildInput( + BuildContext context, + TextEditingController controller, { + int maxLines = 20, + String? hint, + Function(String)? onSubmitted, + bool obscureText = false, + IconData? icon, +}) { return RoundRectCard( TextField( maxLines: maxLines, onSubmitted: onSubmitted, decoration: InputDecoration( - fillColor: Theme.of(context).cardColor, - hintText: hint, - filled: true, - border: InputBorder.none), + hintText: hint, + icon: icon != null ? Icon(icon) : null, + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric(horizontal: 13, vertical: 7) + ), controller: controller, - obscureText: obscureText ?? false, + obscureText: obscureText, ), ); }