ab: opt reaction and message hint

Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
21pages 2023-08-16 10:18:29 +08:00
parent 553a3798a1
commit 1e75b172d6
4 changed files with 85 additions and 32 deletions

View File

@ -48,13 +48,14 @@ class _AddressBookState extends State<AddressBook> {
return Column( return Column(
children: [ children: [
_buildNotEmptyLoading(), _buildNotEmptyLoading(),
_buildRetryProgress(),
_buildErrorBanner( _buildErrorBanner(
err: gFFI.abModel.pullError, err: gFFI.abModel.pullError,
retry: null, retry: null,
close: () => gFFI.abModel.pullError.value = ''), close: () => gFFI.abModel.pullError.value = ''),
_buildErrorBanner( _buildErrorBanner(
err: gFFI.abModel.pushError, err: gFFI.abModel.pushError,
retry: () => gFFI.abModel.pushAb(), retry: () => gFFI.abModel.pushAb(isRetry: true),
close: () => gFFI.abModel.pushError.value = ''), close: () => gFFI.abModel.pushError.value = ''),
Expanded( Expanded(
child: isDesktop child: isDesktop
@ -136,6 +137,13 @@ class _AddressBookState extends State<AddressBook> {
)); ));
} }
Widget _buildRetryProgress() {
return Obx(() => Offstage(
offstage: !gFFI.abModel.retrying.value,
child: LinearProgressIndicator(),
));
}
Widget _buildAddressBookDesktop() { Widget _buildAddressBookDesktop() {
return Row( return Row(
children: [ children: [
@ -339,7 +347,7 @@ class _AddressBookState extends State<AddressBook> {
return; return;
} }
gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag); gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
this.setState(() {}); this.setState(() {});
// final currentPeers // final currentPeers
} }
@ -448,7 +456,7 @@ class _AddressBookState extends State<AddressBook> {
for (final tag in tags) { for (final tag in tags) {
gFFI.abModel.addTag(tag); gFFI.abModel.addTag(tag);
} }
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
// final currentPeers // final currentPeers
} }
close(); close();

View File

@ -584,6 +584,7 @@ abstract class BasePeerCard extends StatelessWidget {
), ),
proc: () { proc: () {
bind.mainCreateShortcut(id: id); bind.mainCreateShortcut(id: id);
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -599,6 +600,7 @@ abstract class BasePeerCard extends StatelessWidget {
setter: (bool v) async { setter: (bool v) async {
await bind.mainSetPeerOption( await bind.mainSetPeerOption(
id: id, key: key, value: bool2option(key, v)); id: id, key: key, value: bool2option(key, v));
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -635,6 +637,7 @@ abstract class BasePeerCard extends StatelessWidget {
id: id, id: id,
key: kOptionForceAlwaysRelay, key: kOptionForceAlwaysRelay,
value: bool2option(kOptionForceAlwaysRelay, v)); value: bool2option(kOptionForceAlwaysRelay, v));
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -659,6 +662,7 @@ abstract class BasePeerCard extends StatelessWidget {
gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
} else { } else {
await bind.mainSetPeerAlias(id: id, alias: newName); await bind.mainSetPeerAlias(id: id, alias: newName);
showToast(translate('Successful'));
_update(); _update();
} }
} }
@ -708,16 +712,25 @@ abstract class BasePeerCard extends StatelessWidget {
break; break;
case PeerTabIndex.ab: case PeerTabIndex.ab:
gFFI.abModel.deletePeer(id); gFFI.abModel.deletePeer(id);
await gFFI.abModel.pushAb(); final future = gFFI.abModel.pushAb();
if (shouldSyncAb() && await bind.mainPeerExists(id: peer.id)) { if (shouldSyncAb() && await bind.mainPeerExists(id: peer.id)) {
BotToast.showText( Future.delayed(Duration.zero, () async {
contentColor: Colors.lightBlue, final succ = await future;
text: translate('synced_peer_readded_tip')); if (succ) {
await Future.delayed(Duration(seconds: 2)); // success msg
BotToast.showText(
contentColor: Colors.lightBlue,
text: translate('synced_peer_readded_tip'));
}
});
} }
break; break;
case PeerTabIndex.group: case PeerTabIndex.group:
break; break;
} }
if (tab != PeerTabIndex.ab) {
showToast(translate('Successful'));
}
} }
deletePeerConfirmDialog(onSubmit, deletePeerConfirmDialog(onSubmit,
@ -737,6 +750,7 @@ abstract class BasePeerCard extends StatelessWidget {
), ),
proc: () { proc: () {
bind.mainForgetPassword(id: id); bind.mainForgetPassword(id: id);
showToast(translate('Successful'));
}, },
padding: menuPadding, padding: menuPadding,
dismissOnClicked: true, dismissOnClicked: true,
@ -769,6 +783,7 @@ abstract class BasePeerCard extends StatelessWidget {
favs.add(id); favs.add(id);
await bind.mainStoreFav(favs: favs); await bind.mainStoreFav(favs: favs);
} }
showToast(translate('Successful'));
}(); }();
}, },
padding: menuPadding, padding: menuPadding,
@ -803,6 +818,7 @@ abstract class BasePeerCard extends StatelessWidget {
await bind.mainStoreFav(favs: favs); await bind.mainStoreFav(favs: favs);
await reloadFunc(); await reloadFunc();
} }
showToast(translate('Successful'));
}(); }();
}, },
padding: menuPadding, padding: menuPadding,
@ -824,7 +840,7 @@ abstract class BasePeerCard extends StatelessWidget {
} }
if (!gFFI.abModel.idContainBy(peer.id)) { if (!gFFI.abModel.idContainBy(peer.id)) {
gFFI.abModel.addPeer(peer); gFFI.abModel.addPeer(peer);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
} }
}(); }();
}, },
@ -1056,7 +1072,7 @@ class AddressBookPeerCard extends BasePeerCard {
proc: () { proc: () {
editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async { editAbTagDialog(gFFI.abModel.getPeerTags(id), (selectedTag) async {
gFFI.abModel.changeTagForPeer(id, selectedTag); gFFI.abModel.changeTagForPeer(id, selectedTag);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
}); });
}, },
padding: super.menuPadding, padding: super.menuPadding,
@ -1128,6 +1144,7 @@ void _rdpDialog(String id) async {
id: id, key: 'rdp_username', value: username); id: id, key: 'rdp_username', value: username);
await bind.mainSetPeerOption( await bind.mainSetPeerOption(
id: id, key: 'rdp_password', value: password); id: id, key: 'rdp_password', value: password);
showToast(translate('Successful'));
close(); close();
} }

View File

@ -343,12 +343,17 @@ class _PeerTabPageState extends State<PeerTabPage>
} }
} }
gFFI.abModel.deletePeers(peers.map((p) => p.id).toList()); gFFI.abModel.deletePeers(peers.map((p) => p.id).toList());
await gFFI.abModel.pushAb(); final future = gFFI.abModel.pushAb();
if (hasSynced) { if (hasSynced) {
Future.delayed(Duration(seconds: 2), () { Future.delayed(Duration.zero, () async {
BotToast.showText( final succ = await future;
contentColor: Colors.lightBlue, if (succ) {
text: translate('synced_peer_readded_tip')); await Future.delayed(
Duration(seconds: 2)); // success msg
BotToast.showText(
contentColor: Colors.lightBlue,
text: translate('synced_peer_readded_tip'));
}
}); });
} }
} }
@ -357,7 +362,7 @@ class _PeerTabPageState extends State<PeerTabPage>
break; break;
} }
gFFI.peerTabModel.setMultiSelectionMode(false); gFFI.peerTabModel.setMultiSelectionMode(false);
showToast(translate('Successful')); if (model.currentTab != 3) showToast(translate('Successful'));
} }
deletePeerConfirmDialog(onSubmit, translate('Delete')); deletePeerConfirmDialog(onSubmit, translate('Delete'));
@ -404,7 +409,6 @@ class _PeerTabPageState extends State<PeerTabPage>
gFFI.abModel.addPeers(peers); gFFI.abModel.addPeers(peers);
gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
model.setMultiSelectionMode(false); model.setMultiSelectionMode(false);
showToast(translate('Successful'));
}, },
child: Tooltip( child: Tooltip(
message: translate('Add to Address Book'), message: translate('Add to Address Book'),

View File

@ -30,6 +30,7 @@ class AbModel {
final tags = [].obs; final tags = [].obs;
final peers = List<Peer>.empty(growable: true).obs; final peers = List<Peer>.empty(growable: true).obs;
final sortTags = shouldSortTags().obs; final sortTags = shouldSortTags().obs;
final retrying = false.obs;
bool get emtpy => peers.isEmpty && tags.isEmpty; bool get emtpy => peers.isEmpty && tags.isEmpty;
final selectedTags = List<String>.empty(growable: true).obs; final selectedTags = List<String>.empty(growable: true).obs;
@ -55,10 +56,11 @@ class AbModel {
if (gFFI.userModel.userName.isEmpty) return; if (gFFI.userModel.userName.isEmpty) return;
if (abLoading.value) return; if (abLoading.value) return;
if (!force && initialized) return; if (!force && initialized) return;
DateTime startTime = DateTime.now();
if (pushError.isNotEmpty) { if (pushError.isNotEmpty) {
try { try {
// push to retry // push to retry
pushAb(toast: false); pushAb(toastIfFail: false, toastIfSucc: false);
} catch (_) {} } catch (_) {}
} }
if (!quiet) { if (!quiet) {
@ -112,14 +114,14 @@ class AbModel {
} }
} }
} finally { } finally {
if (initialized) { var ms =
// make loading effect obvious (Duration(milliseconds: 300) - DateTime.now().difference(startTime))
Future.delayed(Duration(milliseconds: 300), () { .inMilliseconds;
abLoading.value = false; ms = ms > 0 ? ms : 0;
}); Future.delayed(Duration(milliseconds: ms), () {
} else {
abLoading.value = false; abLoading.value = false;
} });
initialized = true; initialized = true;
_syncAllFromRecent = true; _syncAllFromRecent = true;
_timerCounter = 0; _timerCounter = 0;
@ -198,9 +200,16 @@ class AbModel {
it.first.alias = alias; it.first.alias = alias;
} }
Future<void> pushAb({bool toast = true}) async { Future<bool> pushAb(
debugPrint("pushAb"); {bool toastIfFail = true,
bool toastIfSucc = true,
bool isRetry = false}) async {
debugPrint(
"pushAb: toastIfFail:$toastIfFail, toastIfSucc:$toastIfSucc, isRetry:$isRetry");
pushError.value = ''; pushError.value = '';
if (isRetry) retrying.value = true;
DateTime startTime = DateTime.now();
bool ret = false;
try { try {
// avoid double pushes in a row // avoid double pushes in a row
_syncAllFromRecent = true; _syncAllFromRecent = true;
@ -226,12 +235,14 @@ class AbModel {
} }
if (resp.statusCode == 200 && if (resp.statusCode == 200 &&
(resp.body.isEmpty || resp.body.toLowerCase() == 'null')) { (resp.body.isEmpty || resp.body.toLowerCase() == 'null')) {
ret = true;
_saveCache(); _saveCache();
} else { } else {
Map<String, dynamic> json = _jsonDecode(resp.body, resp.statusCode); Map<String, dynamic> json = _jsonDecode(resp.body, resp.statusCode);
if (json.containsKey('error')) { if (json.containsKey('error')) {
throw json['error']; throw json['error'];
} else if (resp.statusCode == 200) { } else if (resp.statusCode == 200) {
ret = true;
_saveCache(); _saveCache();
} else { } else {
throw 'HTTP ${resp.statusCode}'; throw 'HTTP ${resp.statusCode}';
@ -240,12 +251,25 @@ class AbModel {
} catch (e) { } catch (e) {
pushError.value = pushError.value =
'${translate('push_ab_failed_tip')}: ${translate(e.toString())}'; '${translate('push_ab_failed_tip')}: ${translate(e.toString())}';
if (toast && gFFI.peerTabModel.currentTab != PeerTabIndex.ab.index) {
BotToast.showText(contentColor: Colors.red, text: pushError.value);
}
} finally {
_syncAllFromRecent = true;
} }
_syncAllFromRecent = true;
if (isRetry) {
var ms =
(Duration(milliseconds: 200) - DateTime.now().difference(startTime))
.inMilliseconds;
ms = ms > 0 ? ms : 0;
Future.delayed(Duration(milliseconds: ms), () {
retrying.value = false;
});
}
if (!ret && toastIfFail) {
BotToast.showText(contentColor: Colors.red, text: pushError.value);
}
if (ret && toastIfSucc) {
showToast(translate('Successful'));
}
return ret;
} }
Peer? find(String id) { Peer? find(String id) {
@ -410,7 +434,7 @@ class AbModel {
} }
// Be careful with loop calls // Be careful with loop calls
if (syncChanged && push) { if (syncChanged && push) {
pushAb(); pushAb(toastIfSucc: false);
} else if (uiChanged) { } else if (uiChanged) {
peers.refresh(); peers.refresh();
} }