diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 309c51df..c730679b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -470,7 +470,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 525; + CURRENT_PROJECT_VERSION = 526; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -478,7 +478,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.525; + MARKETING_VERSION = 1.0.526; 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 = 525; + CURRENT_PROJECT_VERSION = 526; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -610,7 +610,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.525; + MARKETING_VERSION = 1.0.526; 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 = 525; + CURRENT_PROJECT_VERSION = 526; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -636,7 +636,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.525; + MARKETING_VERSION = 1.0.526; 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 = 525; + CURRENT_PROJECT_VERSION = 526; 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.525; + MARKETING_VERSION = 1.0.526; 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 = 525; + CURRENT_PROJECT_VERSION = 526; 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.525; + MARKETING_VERSION = 1.0.526; 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 = 525; + CURRENT_PROJECT_VERSION = 526; 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.525; + MARKETING_VERSION = 1.0.526; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/lib/app.dart b/lib/app.dart index bf39b183..b34f9046 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -57,7 +57,8 @@ class MyApp extends StatelessWidget { ThemeData _getAmoledTheme(ThemeData darkTheme) => darkTheme.copyWith( scaffoldBackgroundColor: Colors.black, dialogBackgroundColor: Colors.black, - drawerTheme: const DrawerThemeData(backgroundColor: Colors.black), + drawerTheme: const DrawerThemeData( + backgroundColor: Colors.black, shadowColor: Colors.black), appBarTheme: const AppBarTheme(backgroundColor: Colors.black), dialogTheme: const DialogTheme(backgroundColor: Colors.black), bottomSheetTheme: diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 8bb5e76a..4fd06548 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,8 +2,8 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 525; + static const int build = 526; static const String engine = "3.13.1"; - static const String buildAt = "2023-09-03 17:54:05.367410"; - static const int modifications = 4; + static const String buildAt = "2023-09-05 21:15:22.219124"; + static const int modifications = 2; } diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 2178f0fd..94c3a713 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -5,6 +5,7 @@ import 'package:provider/provider.dart'; import 'package:toolbox/core/extension/context.dart'; import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/round_rect_card.dart'; +import 'package:toolbox/view/widget/value_notifier.dart'; import '../../../core/route.dart'; import '../../../core/utils/ui.dart'; @@ -13,7 +14,6 @@ import '../../../data/model/server/server_private_info.dart'; import '../../../data/provider/private_key.dart'; 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.dart'; @@ -30,7 +30,7 @@ class ServerEditPage extends StatefulWidget { class _ServerEditPageState extends State with AfterLayoutMixin { final _nameController = TextEditingController(); final _ipController = TextEditingController(); - final _alterUrlController = TextEditingController(); + final _altUrlController = TextEditingController(); final _portController = TextEditingController(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); @@ -41,27 +41,21 @@ class _ServerEditPageState extends State with AfterLayoutMixin { final _usernameFocus = FocusNode(); late FocusScopeNode _focusScope; - late ServerProvider _serverProvider; late S _s; - final usePublicKey = ValueNotifier(false); - final autoConnect = ValueNotifier(true); - int? _pubKeyIndex; - PrivateKeyInfo? _keyInfo; - List _tags = []; + final _serverProvider = locator(); + final _keyProvider = locator(); - @override - void initState() { - super.initState(); - _serverProvider = locator(); - } + final _keyIdx = ValueNotifier(null); + final _autoConnect = ValueNotifier(true); + List _tags = []; @override void dispose() { super.dispose(); _nameController.dispose(); _ipController.dispose(); - _alterUrlController.dispose(); + _altUrlController.dispose(); _portController.dispose(); _usernameController.dispose(); _passwordController.dispose(); @@ -156,7 +150,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { hint: 'root', ), Input( - controller: _alterUrlController, + controller: _altUrlController, type: TextInputType.text, node: _alterUrlFocus, label: _s.alterUrl, @@ -165,50 +159,29 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ), TagEditor( tags: _tags, - onChanged: (p0) => setState(() { - _tags = p0; - }), + onChanged: (p0) { + setState(() { + _tags = p0; + }); + }, s: _s, tagSuggestions: [..._serverProvider.tags], onRenameTag: _serverProvider.renameTag, ), - width7, - Row( - children: [ - width13, - Text(_s.keyAuth), - width13, - Switch( - value: usePublicKey.value, - onChanged: (val) => setState(() => usePublicKey.value = val), + _buildAuth(), + ListTile( + title: Text(_s.autoConnect), + trailing: ValueBuilder( + listenable: _autoConnect, + build: () => Switch( + value: _autoConnect.value, + onChanged: (val) { + _autoConnect.value = val; + }, ), - ], + ), ), - Row( - children: [ - width13, - Text(_s.autoConnect), - width13, - Switch( - value: autoConnect.value, - onChanged: (val) => setState(() => autoConnect.value = val), - ), - ], - ) ]; - if (usePublicKey.value) { - children.add(_buildKeyAuth()); - } else { - children.add(Input( - controller: _passwordController, - obscureText: true, - type: TextInputType.text, - label: _s.pwd, - icon: Icons.password, - hint: _s.pwd, - onSubmitted: (_) => {}, - )); - } return SingleChildScrollView( padding: const EdgeInsets.fromLTRB(17, 17, 17, 47), child: Column( @@ -219,23 +192,58 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ); } + Widget _buildAuth() { + final switch_ = ListTile( + title: Text(_s.keyAuth), + trailing: ValueBuilder( + listenable: _keyIdx, + build: () => Switch( + value: _keyIdx.value != null, + onChanged: (val) { + if (val) { + _keyIdx.value = -1; + } else { + _keyIdx.value = null; + } + }, + ), + ), + ); + + /// Put [switch_] out of [ValueBuilder] to avoid rebuild + return ValueBuilder( + listenable: _keyIdx, + build: () { + final children = [switch_]; + if (_keyIdx.value != null) { + children.add(_buildKeyAuth()); + } else { + children.add(Input( + controller: _passwordController, + obscureText: true, + type: TextInputType.text, + label: _s.pwd, + icon: Icons.password, + hint: _s.pwd, + onSubmitted: (_) => _onSave(), + )); + } + return Column(children: children); + }, + ); + } + Widget _buildKeyAuth() { return Consumer( builder: (_, key, __) { - for (var item in key.pkis) { - if (item.id == widget.spi?.pubKeyId) { - _pubKeyIndex ??= key.pkis.indexOf(item); - } - } - final tiles = key.pkis - .map( - (e) => ListTile( - contentPadding: EdgeInsets.zero, - title: Text(e.id, textAlign: TextAlign.start), - trailing: _buildRadio(key.pkis.indexOf(e), e), - ), - ) - .toList(); + final tiles = List.generate(key.pkis.length, (index) { + final e = key.pkis[index]; + return ListTile( + contentPadding: EdgeInsets.zero, + title: Text(e.id, textAlign: TextAlign.start), + trailing: _buildRadio(index, e), + ); + }); tiles.add( ListTile( title: Text(_s.addPrivateKey), @@ -248,10 +256,11 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ); return RoundRectCard( Padding( - padding: const EdgeInsets.symmetric(horizontal: 17), - child: Column( - children: tiles, - )), + padding: const EdgeInsets.symmetric(horizontal: 17), + child: Column( + children: tiles, + ), + ), ); }, ); @@ -260,82 +269,21 @@ class _ServerEditPageState extends State with AfterLayoutMixin { Widget _buildFAB() { return FloatingActionButton( heroTag: 'server', + onPressed: _onSave, child: const Icon(Icons.save), - onPressed: () async { - if (_ipController.text == '') { - showSnackBar(context, Text(_s.plzEnterHost)); - return; - } - if (!usePublicKey.value && _passwordController.text == '') { - final cancel = await showRoundDialog( - context: context, - title: Text(_s.attention), - child: Text(_s.sureNoPwd), - actions: [ - TextButton( - onPressed: () => context.pop(false), - child: Text(_s.ok), - ), - TextButton( - onPressed: () => context.pop(true), - child: Text(_s.cancel), - ) - ], - ); - if (cancel ?? true) { - return; - } - } - if (usePublicKey.value && _pubKeyIndex == -1) { - showSnackBar(context, Text(_s.plzSelectKey)); - return; - } - if (_usernameController.text.isEmpty) { - _usernameController.text = 'root'; - } - if (_portController.text.isEmpty) { - _portController.text = '22'; - } - - if (widget.spi != null && widget.spi!.pubKeyId != null) { - _keyInfo ??= locator().get(widget.spi!.pubKeyId!); - } - - final authorization = _passwordController.text; - final spi = ServerPrivateInfo( - name: _nameController.text, - ip: _ipController.text, - port: int.parse(_portController.text), - user: _usernameController.text, - pwd: authorization, - pubKeyId: usePublicKey.value ? _keyInfo!.id : null, - tags: _tags, - alterUrl: - _alterUrlController.text == '' ? null : _alterUrlController.text, - autoConnect: autoConnect.value, - ); - - if (widget.spi == null) { - _serverProvider.addServer(spi); - } else { - _serverProvider.updateServer(widget.spi!, spi); - } - - context.pop(); - }, ); } - Radio _buildRadio(int index, PrivateKeyInfo pki) { - return Radio( - value: index, - groupValue: _pubKeyIndex, - onChanged: (int? value) { - setState(() { - _pubKeyIndex = value!; - _keyInfo = pki; - }); - }, + Widget _buildRadio(int index, PrivateKeyInfo pki) { + return ValueBuilder( + listenable: _keyIdx, + build: () => Radio( + value: index, + groupValue: _keyIdx.value, + onChanged: (value) { + _keyIdx.value = value; + }, + ), ); } @@ -349,14 +297,75 @@ class _ServerEditPageState extends State with AfterLayoutMixin { if (widget.spi?.pubKeyId == null) { _passwordController.text = widget.spi?.pwd ?? ''; } else { - usePublicKey.value = true; + _keyIdx.value = + _keyProvider.pkis.indexWhere((e) => e.id == widget.spi!.pubKeyId); } if (widget.spi?.tags != null) { _tags = widget.spi!.tags!; } - _alterUrlController.text = widget.spi?.alterUrl ?? ''; - autoConnect.value = widget.spi?.autoConnect ?? true; + _altUrlController.text = widget.spi?.alterUrl ?? ''; + _autoConnect.value = widget.spi?.autoConnect ?? true; setState(() {}); } } + + void _onSave() async { + if (_ipController.text == '') { + showSnackBar(context, Text(_s.plzEnterHost)); + return; + } + if (_keyIdx.value == null && _passwordController.text == '') { + final cancel = await showRoundDialog( + context: context, + title: Text(_s.attention), + child: Text(_s.sureNoPwd), + actions: [ + TextButton( + onPressed: () => context.pop(false), + child: Text(_s.ok), + ), + TextButton( + onPressed: () => context.pop(true), + child: Text(_s.cancel), + ) + ], + ); + if (cancel ?? true) { + return; + } + } + // If [_pubKeyIndex] is -1, it means that the user has not selected + if (_keyIdx.value == -1) { + showSnackBar(context, Text(_s.plzSelectKey)); + return; + } + if (_usernameController.text.isEmpty) { + _usernameController.text = 'root'; + } + if (_portController.text.isEmpty) { + _portController.text = '22'; + } + + final spi = ServerPrivateInfo( + name: _nameController.text, + ip: _ipController.text, + port: int.parse(_portController.text), + user: _usernameController.text, + pwd: _passwordController.text.isEmpty ? null : _passwordController.text, + pubKeyId: _keyIdx.value != null + ? _keyProvider.pkis.elementAt(_keyIdx.value!).id + : null, + tags: _tags, + alterUrl: _altUrlController.text.isEmpty ? null : _altUrlController.text, + autoConnect: _autoConnect.value, + ); + + if (widget.spi == null) { + _serverProvider.addServer(spi); + } else { + _serverProvider.updateServer(widget.spi!, spi); + } + + context.pop(); + } } diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index a14e447f..b0553663 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -474,9 +474,9 @@ baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 525; + CURRENT_PROJECT_VERSION = 526; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.525; + MARKETING_VERSION = 1.0.526; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -489,9 +489,9 @@ baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 525; + CURRENT_PROJECT_VERSION = 526; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.525; + MARKETING_VERSION = 1.0.526; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -504,9 +504,9 @@ baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 525; + CURRENT_PROJECT_VERSION = 526; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.525; + MARKETING_VERSION = 1.0.526; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0;