From 806a223e009d9be4e25c1611cf31f6d46f9a29f5 Mon Sep 17 00:00:00 2001 From: lollipopkit Date: Wed, 6 Sep 2023 20:54:17 +0800 Subject: [PATCH] fix: `tag` changed without saving --- lib/data/model/server/private_key_info.dart | 13 +++ lib/data/store/setting.dart | 17 +++- lib/view/page/private_key/edit.dart | 32 +++---- lib/view/page/private_key/list.dart | 16 ++-- lib/view/page/server/edit.dart | 94 ++++++++++++--------- lib/view/page/snippet/edit.dart | 2 +- lib/view/widget/tag.dart | 72 ++++++++-------- 7 files changed, 143 insertions(+), 103 deletions(-) diff --git a/lib/data/model/server/private_key_info.dart b/lib/data/model/server/private_key_info.dart index 1450231e..1b856e8b 100644 --- a/lib/data/model/server/private_key_info.dart +++ b/lib/data/model/server/private_key_info.dart @@ -14,6 +14,19 @@ class PrivateKeyInfo { required this.key, }); + String? get type { + final lines = key.split('\n'); + if (lines.length < 2) { + return null; + } + final firstLine = lines[0]; + final splited = firstLine.split(RegExp(r'\s+')); + if (splited.length < 2) { + return null; + } + return splited[1]; + } + PrivateKeyInfo.fromJson(Map json) : id = json["id"].toString(), key = json["private_key"].toString(); diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index b607b82b..d318996d 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -6,10 +6,13 @@ import '../model/app/net_view.dart'; import '../res/default.dart'; class SettingStore extends PersistentStore { + /// Convert all settings into json + Map toJson() => {for (var e in box.keys) e: box.get(e)}; + // ------BEGIN------ // These settings are not displayed in the settings page // You can edit them in the settings json editor (by long press the settings - // item in the drawer of the server tab page) + // item in the drawer of the home page) /// Discussion #146 late final serverTabUseOldUI = StoreProperty( @@ -31,10 +34,16 @@ class SettingStore extends PersistentStore { 'recordHistory', true, ); - // ------END------ - /// Convert all settings into json - Map toJson() => {for (var e in box.keys) e: box.get(e)}; + /// Bigger for bigger font size + /// 1.0 means 100% + /// Warning: This may cause some UI issues + late final textFactor = StoreProperty( + box, + 'textFactor', + 1.0, + ); + // ------END------ late final primaryColor = StoreProperty( box, diff --git a/lib/view/page/private_key/edit.dart b/lib/view/page/private_key/edit.dart index 77fc6ab7..5502fd41 100644 --- a/lib/view/page/private_key/edit.dart +++ b/lib/view/page/private_key/edit.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:after_layout/after_layout.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -30,8 +29,7 @@ class PrivateKeyEditPage extends StatefulWidget { _PrivateKeyEditPageState createState() => _PrivateKeyEditPageState(); } -class _PrivateKeyEditPageState extends State - with AfterLayoutMixin { +class _PrivateKeyEditPageState extends State { final _nameController = TextEditingController(); final _keyController = TextEditingController(); final _pwdController = TextEditingController(); @@ -40,7 +38,7 @@ class _PrivateKeyEditPageState extends State final _pwdNode = FocusNode(); late FocusScopeNode _focusScope; - late PrivateKeyProvider _provider; + final _provider = locator(); late S _s; Widget? _loading; @@ -48,7 +46,18 @@ class _PrivateKeyEditPageState extends State @override void initState() { super.initState(); - _provider = locator(); + if (widget.pki != null) { + _nameController.text = widget.pki!.id; + _keyController.text = widget.pki!.key; + } else { + Clipboard.getData(_format).then((value) { + if (value == null) return; + final clipdata = value.text?.trim() ?? ''; + if (clipdata.startsWith('-----BEGIN') && clipdata.endsWith('-----')) { + _keyController.text = clipdata; + } + }); + } } @override @@ -216,17 +225,4 @@ class _PrivateKeyEditPageState extends State ], ); } - - @override - Future afterFirstLayout(BuildContext context) async { - if (widget.pki != null) { - _nameController.text = widget.pki!.id; - _keyController.text = widget.pki!.key; - } else { - final clipdata = ((await Clipboard.getData(_format))?.text ?? '').trim(); - if (clipdata.startsWith('-----BEGIN') && clipdata.endsWith('-----')) { - _keyController.text = clipdata; - } - } - } } diff --git a/lib/view/page/private_key/list.dart b/lib/view/page/private_key/list.dart index 3201cef4..cb5be8d9 100644 --- a/lib/view/page/private_key/list.dart +++ b/lib/view/page/private_key/list.dart @@ -60,14 +60,20 @@ class _PrivateKeyListState extends State padding: const EdgeInsets.all(13), itemCount: key.pkis.length, itemBuilder: (context, idx) { + final item = key.pkis[idx]; return RoundRectCard( ListTile( - title: Text(key.pkis[idx].id), - trailing: TextButton( - onPressed: () => - AppRoute.keyEdit(pki: key.pkis[idx]).go(context), - child: Text(_s.edit), + leading: Text( + '#$idx', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), ), + title: Text(item.id), + subtitle: Text(item.type ?? _s.unknown, style: grey), + onTap: () => AppRoute.keyEdit(pki: item).go(context), + trailing: const Icon(Icons.edit), ), ); }, diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 94c3a713..91154c35 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -1,11 +1,7 @@ -import 'package:after_layout/after_layout.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; 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'; @@ -16,7 +12,10 @@ import '../../../data/provider/server.dart'; import '../../../data/res/ui.dart'; import '../../../locator.dart'; import '../../widget/custom_appbar.dart'; +import '../../widget/input_field.dart'; +import '../../widget/round_rect_card.dart'; import '../../widget/tag.dart'; +import '../../widget/value_notifier.dart'; class ServerEditPage extends StatefulWidget { const ServerEditPage({Key? key, this.spi}) : super(key: key); @@ -27,7 +26,7 @@ class ServerEditPage extends StatefulWidget { _ServerEditPageState createState() => _ServerEditPageState(); } -class _ServerEditPageState extends State with AfterLayoutMixin { +class _ServerEditPageState extends State { final _nameController = TextEditingController(); final _ipController = TextEditingController(); final _altUrlController = TextEditingController(); @@ -43,12 +42,42 @@ class _ServerEditPageState extends State with AfterLayoutMixin { late FocusScopeNode _focusScope; late S _s; - final _serverProvider = locator(); - final _keyProvider = locator(); + final _srvs = locator(); + final _keys = locator(); final _keyIdx = ValueNotifier(null); final _autoConnect = ValueNotifier(true); - List _tags = []; + + var _tags = []; + + @override + void initState() { + super.initState(); + + if (widget.spi != null) { + _nameController.text = widget.spi?.name ?? ''; + _ipController.text = widget.spi?.ip ?? ''; + _portController.text = (widget.spi?.port ?? 22).toString(); + _usernameController.text = widget.spi?.user ?? ''; + if (widget.spi?.pubKeyId == null) { + _passwordController.text = widget.spi?.pwd ?? ''; + } else { + _keyIdx.value = _keys.pkis.indexWhere( + (e) => e.id == widget.spi!.pubKeyId, + ); + } + if (widget.spi?.tags != null) { + /// List in dart is passed by pointer, so you need to copy it here + _tags.addAll(widget.spi!.tags!); + } + if (widget.spi?.alterUrl != null) { + _altUrlController.text = widget.spi!.alterUrl!; + } + if (widget.spi?.autoConnect != null) { + _autoConnect.value = widget.spi!.autoConnect!; + } + } + } @override void dispose() { @@ -92,7 +121,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { actions: [ TextButton( onPressed: () { - _serverProvider.delServer(widget.spi!.id); + _srvs.delServer(widget.spi!.id); context.pop(); context.pop(true); }, @@ -159,14 +188,10 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ), TagEditor( tags: _tags, - onChanged: (p0) { - setState(() { - _tags = p0; - }); - }, + onChanged: (p0) => _tags = p0, s: _s, - tagSuggestions: [..._serverProvider.tags], - onRenameTag: _serverProvider.renameTag, + allTags: [..._srvs.tags], + onRenameTag: _srvs.renameTag, ), _buildAuth(), ListTile( @@ -240,7 +265,16 @@ class _ServerEditPageState extends State with AfterLayoutMixin { final e = key.pkis[index]; return ListTile( contentPadding: EdgeInsets.zero, + leading: Text( + '#${index + 1}', + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + ), title: Text(e.id, textAlign: TextAlign.start), + subtitle: Text( + e.type ?? _s.unknown, + textAlign: TextAlign.start, + style: grey, + ), trailing: _buildRadio(index, e), ); }); @@ -287,28 +321,6 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ); } - @override - void afterFirstLayout(BuildContext context) { - if (widget.spi != null) { - _nameController.text = widget.spi?.name ?? ''; - _ipController.text = widget.spi?.ip ?? ''; - _portController.text = (widget.spi?.port ?? 22).toString(); - _usernameController.text = widget.spi?.user ?? ''; - if (widget.spi?.pubKeyId == null) { - _passwordController.text = widget.spi?.pwd ?? ''; - } else { - _keyIdx.value = - _keyProvider.pkis.indexWhere((e) => e.id == widget.spi!.pubKeyId); - } - if (widget.spi?.tags != null) { - _tags = widget.spi!.tags!; - } - _altUrlController.text = widget.spi?.alterUrl ?? ''; - _autoConnect.value = widget.spi?.autoConnect ?? true; - setState(() {}); - } - } - void _onSave() async { if (_ipController.text == '') { showSnackBar(context, Text(_s.plzEnterHost)); @@ -353,7 +365,7 @@ class _ServerEditPageState extends State with AfterLayoutMixin { user: _usernameController.text, pwd: _passwordController.text.isEmpty ? null : _passwordController.text, pubKeyId: _keyIdx.value != null - ? _keyProvider.pkis.elementAt(_keyIdx.value!).id + ? _keys.pkis.elementAt(_keyIdx.value!).id : null, tags: _tags, alterUrl: _altUrlController.text.isEmpty ? null : _altUrlController.text, @@ -361,9 +373,9 @@ class _ServerEditPageState extends State with AfterLayoutMixin { ); if (widget.spi == null) { - _serverProvider.addServer(spi); + _srvs.addServer(spi); } else { - _serverProvider.updateServer(widget.spi!, spi); + _srvs.updateServer(widget.spi!, spi); } context.pop(); diff --git a/lib/view/page/snippet/edit.dart b/lib/view/page/snippet/edit.dart index e9f76469..3b59901b 100644 --- a/lib/view/page/snippet/edit.dart +++ b/lib/view/page/snippet/edit.dart @@ -129,7 +129,7 @@ class _SnippetEditPageState extends State _tags = p0; }), s: _s, - tagSuggestions: [..._provider.tags], + allTags: [..._provider.tags], onRenameTag: (old, n) => setState(() { _provider.renameTag(old, n); }), diff --git a/lib/view/widget/tag.dart b/lib/view/widget/tag.dart index 7c329b57..83d499f3 100644 --- a/lib/view/widget/tag.dart +++ b/lib/view/widget/tag.dart @@ -37,12 +37,12 @@ class TagBtn extends StatelessWidget { } } -class TagEditor extends StatelessWidget { +class TagEditor extends StatefulWidget { final List tags; final S s; final void Function(List)? onChanged; final void Function(String old, String new_)? onRenameTag; - final List? tagSuggestions; + final List allTags; const TagEditor({ super.key, @@ -50,40 +50,46 @@ class TagEditor extends StatelessWidget { required this.s, this.onChanged, this.onRenameTag, - this.tagSuggestions, + this.allTags = const [], }); + @override + State createState() => _TagEditorState(); +} + +class _TagEditorState extends State { @override Widget build(BuildContext context) { return RoundRectCard(ListTile( leading: const Icon(Icons.tag), - title: _buildTags(context, tags), + title: _buildTags(widget.tags), trailing: InkWell( child: const Icon(Icons.add), onTap: () { - _showAddTagDialog(context, tags, onChanged); + _showAddTagDialog(); }, ), )); } - Widget _buildTags(BuildContext context, List tags) { - tagSuggestions?.removeWhere((element) => tags.contains(element)); - final suggestionLen = tagSuggestions?.length ?? 0; + Widget _buildTags(List tags) { + final suggestions = widget.allTags.where((e) => !tags.contains(e)).toList(); + final suggestionLen = suggestions.length; + + /// Add vertical divider if suggestions.length > 0 final counts = tags.length + suggestionLen + (suggestionLen == 0 ? 0 : 1); - if (counts == 0) return Text(s.tag); + if (counts == 0) return Text(widget.s.tag); return ConstrainedBox( constraints: const BoxConstraints(maxHeight: _kTagBtnHeight), child: ListView.builder( scrollDirection: Axis.horizontal, itemBuilder: (context, index) { if (index < tags.length) { - return _buildTagItem(context, tags[index], false); + return _buildTagItem(tags[index]); } else if (index > tags.length) { return _buildTagItem( - context, - tagSuggestions![index - tags.length - 1], - true, + suggestions[index - tags.length - 1], + isAdd: true, ); } return const VerticalDivider(); @@ -93,7 +99,7 @@ class TagEditor extends StatelessWidget { ); } - Widget _buildTagItem(BuildContext context, String tag, bool isAdd) { + Widget _buildTagItem(String tag, {bool isAdd = false}) { return _wrap( Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -114,65 +120,63 @@ class TagEditor extends StatelessWidget { ), onTap: () { if (isAdd) { - tags.add(tag); + widget.tags.add(tag); } else { - tags.remove(tag); + widget.tags.remove(tag); } - onChanged?.call(tags); + widget.onChanged?.call(widget.tags); + setState(() {}); }, - onLongPress: () => _showRenameDialog(context, tag), + onLongPress: () => _showRenameDialog(tag), ); } - void _showAddTagDialog( - BuildContext context, - List tags, - void Function(List)? onChanged, - ) { + void _showAddTagDialog() { final textEditingController = TextEditingController(); showRoundDialog( context: context, - title: Text(s.add), + title: Text(widget.s.add), child: Input( autoFocus: true, icon: Icons.tag, controller: textEditingController, - hint: s.tag, + hint: widget.s.tag, ), actions: [ TextButton( onPressed: () { final tag = textEditingController.text; - tags.add(tag.trim()); - onChanged?.call(tags); - Navigator.pop(context); + widget.tags.add(tag.trim()); + widget.onChanged?.call(widget.tags); + context.pop(); }, - child: Text(s.add), + child: Text(widget.s.add), ), ], ); } - void _showRenameDialog(BuildContext context, String tag) { + void _showRenameDialog(String tag) { final textEditingController = TextEditingController(text: tag); showRoundDialog( context: context, - title: Text(s.rename), + title: Text(widget.s.rename), child: Input( autoFocus: true, icon: Icons.abc, controller: textEditingController, - hint: s.tag, + hint: widget.s.tag, ), actions: [ TextButton( onPressed: () { final newTag = textEditingController.text.trim(); if (newTag.isEmpty) return; - onRenameTag?.call(tag, newTag); + widget.onRenameTag?.call(tag, newTag); context.pop(); + setState(() {}); }, - child: Text(s.rename), + child: Text(widget.s.rename), ), ], );