diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index c62a8842..fa7d15b7 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -9,6 +9,7 @@ import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import 'package:get/get.dart'; import '../../common.dart'; +import 'dialog.dart'; import 'login.dart'; final hideAbTagsPanel = false.obs; @@ -473,6 +474,29 @@ class AddressBookTag extends StatelessWidget { void _showMenu(BuildContext context, RelativeRect pos) { final items = [ + getEntry(translate("Rename"), () { + renameDialog( + oldName: name, + validator: (String? newName) { + if (newName == null || newName.isEmpty) { + return translate('Can not be empty'); + } + if (newName != name && gFFI.abModel.tags.contains(newName)) { + return translate('Already exists'); + } + return null; + }, + onSubmit: (String newName) { + if (name != newName) { + gFFI.abModel.renameTag(name, newName); + gFFI.abModel.pushAb(); + } + Future.delayed(Duration.zero, () => Get.back()); + }, + onCancel: () { + Future.delayed(Duration.zero, () => Get.back()); + }); + }), getEntry(translate("Delete"), () { gFFI.abModel.deleteTag(name); gFFI.abModel.pushAb(); diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 77307418..40f0901a 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -1448,3 +1448,74 @@ void editAbTagDialog( ); }); } + +void renameDialog( + {required String oldName, + FormFieldValidator? validator, + required ValueChanged onSubmit, + Function? onCancel}) async { + RxBool isInProgress = false.obs; + var controller = TextEditingController(text: oldName); + final formKey = GlobalKey(); + gFFI.dialogManager.show((setState, close, context) { + submit() async { + String text = controller.text.trim(); + if (validator != null && formKey.currentState?.validate() == false) { + return; + } + isInProgress.value = true; + onSubmit(text); + close(); + isInProgress.value = false; + } + + cancel() { + onCancel?.call(); + close(); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.edit_rounded, color: MyTheme.accent), + Text(translate('Rename')).paddingOnly(left: 10), + ], + ), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Form( + key: formKey, + child: TextFormField( + controller: controller, + autofocus: true, + decoration: InputDecoration(labelText: translate('Name')), + validator: validator, + ), + ), + ), + Obx(() => Offstage( + offstage: isInProgress.isFalse, + child: const LinearProgressIndicator())), + ], + ), + actions: [ + dialogButton( + "Cancel", + icon: Icon(Icons.close_rounded), + onPressed: cancel, + isOutline: true, + ), + dialogButton( + "OK", + icon: Icon(Icons.done_rounded), + onPressed: submit, + ), + ], + onSubmit: submit, + onCancel: cancel, + ); + }); +} diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index f12c1516..4d49bd49 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -139,9 +139,10 @@ class _PeerCardState extends State<_PeerCard> ); }, child: GestureDetector( - onDoubleTap: peerTabModel.multiSelectionMode - ? null - : () => widget.connect(context, peer.id), + onDoubleTap: + peerTabModel.multiSelectionMode || peerTabModel.isShiftDown + ? null + : () => widget.connect(context, peer.id), onTap: () => peerTabModel.select(peer), onLongPress: () => peerTabModel.select(peer), child: Obx(() => peerCardUiType.value == PeerUiType.grid @@ -600,8 +601,16 @@ abstract class BasePeerCard extends StatelessWidget { translate('Rename'), style: style, ), - proc: () { - _rename(id); + proc: () async { + String oldName = await _getAlias(id); + renameDialog( + oldName: oldName, + onSubmit: (String newName) async { + if (newName != oldName) { + await bind.mainSetPeerAlias(id: id, alias: newName); + _update(); + } + }); }, padding: menuPadding, dismissOnClicked: true, @@ -771,64 +780,6 @@ abstract class BasePeerCard extends StatelessWidget { Future _getAlias(String id) async => await bind.mainGetPeerOption(id: id, key: 'alias'); - void _rename(String id) async { - RxBool isInProgress = false.obs; - String name = await _getAlias(id); - var controller = TextEditingController(text: name); - gFFI.dialogManager.show((setState, close, context) { - submit() async { - isInProgress.value = true; - String name = controller.text.trim(); - await bind.mainSetPeerAlias(id: id, alias: name); - _update(); - close(); - isInProgress.value = false; - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.edit_rounded, color: MyTheme.accent), - Text(translate('Rename')).paddingOnly(left: 10), - ], - ), - content: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - child: Form( - child: TextFormField( - controller: controller, - autofocus: true, - decoration: InputDecoration(labelText: translate('Name')), - ), - ), - ), - Obx(() => Offstage( - offstage: isInProgress.isFalse, - child: const LinearProgressIndicator())), - ], - ), - actions: [ - dialogButton( - "Cancel", - icon: Icon(Icons.close_rounded), - onPressed: close, - isOutline: true, - ), - dialogButton( - "OK", - icon: Icon(Icons.done_rounded), - onPressed: submit, - ), - ], - onSubmit: submit, - onCancel: close, - ); - }); - } - @protected void _update(); } diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 5bbbc0f9..be801a14 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -223,6 +223,33 @@ class AbModel { } } + void renameTag(String oldTag, String newTag) { + if (tags.contains(newTag)) return; + tags.value = tags.map((e) { + if (e == oldTag) { + return newTag; + } else { + return oldTag; + } + }).toList(); + selectedTags.value = selectedTags.map((e) { + if (e == oldTag) { + return newTag; + } else { + return oldTag; + } + }).toList(); + for (var peer in peers) { + peer.tags = peer.tags.map((e) { + if (e == oldTag) { + return newTag; + } else { + return oldTag; + } + }).toList(); + } + } + void unsetSelectedTags() { selectedTags.clear(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index b3d913b6..23914b81 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 83060c6a..6c4565fe 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "对标签进行排序"), ("Open connection in new tab", "在选项卡中打开新连接"), ("Move tab to new window", "将标签页移至新窗口"), + ("Can not be empty", "不能为空"), + ("Already exists", "已经存在"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 2d7a7ce1..b2a304c3 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 0a10551d..df3aecc4 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index ffb62a45..c3351013 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "Tags sortieren"), ("Open connection in new tab", "Verbindung in neuem Tab öffnen"), ("Move tab to new window", "Tab in neues Fenster verschieben"), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 33f4d74b..846c632c 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 8b29e418..aec715f7 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 27752e98..dcd39c9a 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "Ordenar etiquetas"), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 0b56e073..7cda61f8 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 8a2baef5..3acbdf80 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 36c43fec..44158db0 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index c65eee87..dac33d1f 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 5a655ce5..8dac0ba7 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "Ordina etichette"), ("Open connection in new tab", "Apri connessione in una nuova scheda"), ("Move tab to new window", "Sposta scheda nella finestra successiva"), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 6ece9d5c..5099a838 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 6ea9b94e..43cc3526 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index e5d035b0..ea08bfc5 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 47b70acf..4cbf215a 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 08934cea..312def15 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "Labels sorteren"), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 84f13c61..cdab09c7 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "Znaczniki sortowania"), ("Open connection in new tab", ""), ("Move tab to new window", "Przenieś zakładkę do nowego okna"), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index b9e15536..7bfad8a1 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 81fd68ac..df0e922b 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index ddd141fa..0e280496 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index c0f129ab..ea51c660 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "Сортировка меток"), ("Open connection in new tab", "Открыть подключение в новой вкладке"), ("Move tab to new window", "Переместить вкладку в отдельное окно"), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index afe52f4a..487edfbf 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index dc79f9e4..dc51191b 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 86f3f354..7ac82ee6 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 06667f4c..686d0c66 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 2e8ea871..072baf20 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ed3f41ef..ff87844c 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 09ade050..4f462ef6 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 629a144f..b184dff2 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 80e4f31c..2f374329 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -529,5 +529,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", "排序標籤"), ("Open connection in new tab", "在新分頁開啟連線"), ("Move tab to new window", "移動標籤到新視窗"), + ("Can not be empty", "不能為空"), + ("Already exists", "已經存在"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index a12e2fd1..ac5e0b67 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 0db48584..335f25a5 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -526,5 +526,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Sort tags", ""), ("Open connection in new tab", ""), ("Move tab to new window", ""), + ("Can not be empty", ""), + ("Already exists", ""), ].iter().cloned().collect(); }