From bea88f31e0c1986567971aaad697da69fcf2de4a Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 11 Aug 2023 15:27:50 +0800 Subject: [PATCH] use ab cache init show and show custom loading when ab not emtpy Signed-off-by: 21pages --- flutter/lib/common/widgets/address_book.dart | 31 +++++++-- .../common/widgets/loading_dot_widget.dart | 65 +++++++++++++++++++ flutter/lib/common/widgets/peer_card.dart | 2 +- flutter/lib/models/ab_model.dart | 53 +++++++++++---- libs/hbb_common/src/config.rs | 22 +++++++ src/flutter_ffi.rs | 4 ++ 6 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 flutter/lib/common/widgets/loading_dot_widget.dart diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index fa7d15b73..f9ad6166e 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -10,6 +10,7 @@ import 'package:get/get.dart'; import '../../common.dart'; import 'dialog.dart'; +import 'loading_dot_widget.dart'; import 'login.dart'; final hideAbTagsPanel = false.obs; @@ -39,7 +40,7 @@ class _AddressBookState extends State { child: ElevatedButton( onPressed: loginDialog, child: Text(translate("Login")))); } else { - if (gFFI.abModel.abLoading.value) { + if (gFFI.abModel.abLoading.value && gFFI.abModel.emtpy) { return const Center( child: CircularProgressIndicator(), ); @@ -47,9 +48,15 @@ class _AddressBookState extends State { if (gFFI.abModel.abError.isNotEmpty) { return _buildShowError(gFFI.abModel.abError.value); } - return isDesktop - ? _buildAddressBookDesktop() - : _buildAddressBookMobile(); + return Column( + children: [ + _buildLoadingHavingPeers(), + Expanded( + child: isDesktop + ? _buildAddressBookDesktop() + : _buildAddressBookMobile()) + ], + ); } }); @@ -68,6 +75,22 @@ class _AddressBookState extends State { )); } + Widget _buildLoadingHavingPeers() { + double size = 15; + return Obx(() => Offstage( + offstage: !(gFFI.abModel.abLoading.value && !gFFI.abModel.emtpy), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: size, + child: Center(child: LoadingDotWidget(size: size))) + .marginSymmetric(vertical: 10) + ], + ), + )); + } + Widget _buildAddressBookDesktop() { return Row( children: [ diff --git a/flutter/lib/common/widgets/loading_dot_widget.dart b/flutter/lib/common/widgets/loading_dot_widget.dart new file mode 100644 index 000000000..6f8ca4425 --- /dev/null +++ b/flutter/lib/common/widgets/loading_dot_widget.dart @@ -0,0 +1,65 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class LoadingDotWidget extends StatefulWidget { + final int count; + final double size; + final int duration; + LoadingDotWidget( + {Key? key, required this.size, this.count = 3, this.duration = 200}) + : super(key: key); + + @override + State createState() => _LoadingDotWidgetState(); +} + +class _LoadingDotWidgetState extends State { + int counter = 0; + Timer? timer; + + @override + void initState() { + super.initState(); + startAnimation(); + } + + @override + void dispose() { + timer?.cancel(); + super.dispose(); + } + + void startAnimation() { + timer = Timer.periodic(Duration(milliseconds: widget.duration), (timer) { + if (mounted) { + setState(() { + counter = (counter + 1) % widget.count; + }); + } + }); + } + + @override + Widget build(BuildContext context) { + getChild(int index) { + return AnimatedContainer( + duration: Duration(milliseconds: widget.duration), + width: counter == index ? widget.size : widget.size / 2, + height: counter == index ? widget.size : widget.size / 2, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey, + ), + ).marginSymmetric(horizontal: widget.size); + } + + return Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(widget.count, (e) => e) + .map((e) => getChild(e)) + .toList()), + ); + } +} diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 4d49bd496..f71588b7d 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -987,7 +987,7 @@ class AddressBookPeerCard extends BasePeerCard { @protected @override - void _update() => gFFI.abModel.pullAb(); + void _update() => gFFI.abModel.pullAb(quiet: true); @protected MenuEntryBase _editTagAction(String id) { diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index cd9a41252..9a7a42d31 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -28,6 +28,7 @@ class AbModel { final tags = [].obs; final peers = List.empty(growable: true).obs; final sortTags = shouldSortTags().obs; + bool get emtpy => peers.isEmpty && tags.isEmpty; final selectedTags = List.empty(growable: true).obs; var initialized = false; @@ -46,10 +47,13 @@ class AbModel { } Future pullAb({force = true, quiet = false}) async { - debugPrint("pullAb, force:$force, quite:$quiet"); + debugPrint("pullAb, force:$force, quiet:$quiet"); if (gFFI.userModel.userName.isEmpty) return; if (abLoading.value) return; if (!force && initialized) return; + if (!initialized) { + await _loadCache(); + } if (!quiet) { abLoading.value = true; abError.value = ""; @@ -92,14 +96,21 @@ class AbModel { peers.add(Peer.fromJson(peer)); } } - save(); // save on success + _saveCache(); // save on success } } } } catch (err) { abError.value = err.toString(); } finally { - abLoading.value = false; + if (initialized) { + // make loading effect obvious + Future.delayed(Duration(milliseconds: 300), () { + abLoading.value = false; + }); + } else { + abLoading.value = false; + } initialized = true; sync_all_from_recent = true; _timerCounter = 0; @@ -197,7 +208,7 @@ class AbModel { try { await http.Client().send(request); // await pullAb(quiet: true); - save(); // save on success + _saveCache(); // save on success } catch (e) { BotToast.showText(contentColor: Colors.red, text: e.toString()); } finally { @@ -369,21 +380,39 @@ class AbModel { } } - save() { + _saveCache() { try { - final infos = peers - .map((e) => ({ - "id": e.id, - "hash": e.hash, - })) - .toList(); + final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList(); final m = { "access_token": bind.mainGetLocalOption(key: 'access_token'), - "peers": infos, + "peers": peersJsonData, + "tags": tags.map((e) => e.toString()).toList(), }; bind.mainSaveAb(json: jsonEncode(m)); } catch (e) { debugPrint('ab save:$e'); } } + + _loadCache() async { + try { + final access_token = bind.mainGetLocalOption(key: 'access_token'); + if (access_token.isEmpty) return; + final cache = await bind.mainLoadAb(); + final data = jsonDecode(cache); + if (data == null || data['access_token'] != access_token) return; + tags.clear(); + peers.clear(); + if (data['tags'] is List) { + tags.value = data['tags']; + } + if (data['peers'] is List) { + for (final peer in data['peers']) { + peers.add(Peer.fromJson(peer)); + } + } + } catch (e) { + debugPrint("load ab cache: $e"); + } + } } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index c3aa2cfd9..cd114619f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1481,6 +1481,26 @@ pub struct AbPeer { skip_serializing_if = "String::is_empty" )] pub hash: String, + #[serde( + default, + deserialize_with = "deserialize_string", + skip_serializing_if = "String::is_empty" + )] + pub username: String, + #[serde( + default, + deserialize_with = "deserialize_string", + skip_serializing_if = "String::is_empty" + )] + pub hostname: String, + #[serde( + default, + deserialize_with = "deserialize_string", + skip_serializing_if = "String::is_empty" + )] + pub platform: String, + #[serde(default, deserialize_with = "deserialize_vec_string")] + pub tags: Vec, } #[derive(Debug, Default, Serialize, Deserialize, Clone)] @@ -1493,6 +1513,8 @@ pub struct Ab { pub access_token: String, #[serde(default, deserialize_with = "deserialize_vec_abpeer")] pub peers: Vec, + #[serde(default, deserialize_with = "deserialize_vec_string")] + pub tags: Vec, } impl Ab { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 7f7af71b4..ab7acc104 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1165,6 +1165,10 @@ pub fn main_clear_ab() { config::Ab::remove(); } +pub fn main_load_ab() -> String { + serde_json::to_string(&config::Ab::load()).unwrap_or_default() +} + pub fn session_send_pointer(session_id: SessionID, msg: String) { if let Ok(m) = serde_json::from_str::>(&msg) { let alt = m.get("alt").is_some();