ab show loading LinearProgressIndicator (#7538)

* Display the LinearProgressIndicator at the top of ab if the current ab is both empty and in a loading state.
* Add pull error like the legacy ab.
* When forcefully reading the 'ab' list, retrieve the personal ab guid to judge the current mode  to prevent server upgrades while the main window remains open

Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
21pages 2024-03-27 21:28:21 +08:00 committed by GitHub
parent cc30f7aa02
commit 22356940d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 41 additions and 21 deletions

View File

@ -48,6 +48,10 @@ class _AddressBookState extends State<AddressBook> {
} else { } else {
return Column( return Column(
children: [ children: [
// NOT use Offstage to wrap LinearProgressIndicator
if (gFFI.abModel.currentAbLoading.value &&
gFFI.abModel.currentAbEmpty)
const LinearProgressIndicator(),
buildErrorBanner(context, buildErrorBanner(context,
loading: gFFI.abModel.currentAbLoading, loading: gFFI.abModel.currentAbLoading,
err: gFFI.abModel.currentAbPullError, err: gFFI.abModel.currentAbPullError,

View File

@ -49,11 +49,11 @@ class AbModel {
RxList<String> get selectedTags => current.selectedTags; RxList<String> get selectedTags => current.selectedTags;
RxBool get currentAbLoading => current.abLoading; RxBool get currentAbLoading => current.abLoading;
bool get currentAbEmpty => current.peers.isEmpty && current.tags.isEmpty;
RxString get currentAbPullError => current.pullError; RxString get currentAbPullError => current.pullError;
RxString get currentAbPushError => current.pushError; RxString get currentAbPushError => current.pushError;
String? _personalAbGuid; String? _personalAbGuid;
RxBool legacyMode = true.obs; RxBool legacyMode = true.obs;
var _modeTested = false; // whether the mode has been tested
final sortTags = shouldSortTags().obs; final sortTags = shouldSortTags().obs;
final filterByIntersection = filterAbTagByIntersection().obs; final filterByIntersection = filterAbTagByIntersection().obs;
@ -83,7 +83,6 @@ class AbModel {
reset() async { reset() async {
print("reset ab model"); print("reset ab model");
_modeTested = false;
addressbooks.clear(); addressbooks.clear();
setCurrentName(''); setCurrentName('');
await bind.mainClearAb(); await bind.mainClearAb();
@ -114,23 +113,16 @@ class AbModel {
debugPrint("pullAb, force: $force, quiet: $quiet"); debugPrint("pullAb, force: $force, quiet: $quiet");
if (!gFFI.userModel.isLogin) return; if (!gFFI.userModel.isLogin) return;
if (force == null && listInitialized && current.initialized) return; if (force == null && listInitialized && current.initialized) return;
try { if (!listInitialized || force == ForcePullAb.listAndCurrent) {
if (!_modeTested) { try {
// Get personal address book guid // Read personal guid every time to avoid upgrading the server without closing the main window
_personalAbGuid = null; _personalAbGuid = null;
await _getPersonalAbGuid(); await _getPersonalAbGuid();
// Determine legacy mode based on whether _personalAbGuid is null // Determine legacy mode based on whether _personalAbGuid is null
legacyMode.value = _personalAbGuid == null; legacyMode.value = _personalAbGuid == null;
_modeTested = true; if (!legacyMode.value && _maxPeerOneAb == 0) {
if (!legacyMode.value) {
await _getAbSettings(); await _getAbSettings();
} }
}
} catch (e) {
debugPrint("test ab mode error: $e");
}
if (!listInitialized || force == ForcePullAb.listAndCurrent) {
try {
if (_personalAbGuid != null) { if (_personalAbGuid != null) {
debugPrint("pull ab list"); debugPrint("pull ab list");
List<AbProfile> abProfiles = List.empty(growable: true); List<AbProfile> abProfiles = List.empty(growable: true);
@ -664,7 +656,7 @@ class AbModel {
} }
} }
if (!current.initialized) { if (!current.initialized) {
await current.pullAb(quiet: true); await current.pullAb(quiet: false);
_saveCache(); _saveCache();
} }
_refreshTab(); _refreshTab();
@ -755,7 +747,9 @@ abstract class BaseAb {
pullError.value = ""; pullError.value = "";
} }
initialized = false; initialized = false;
initialized = await pullAbImpl(quiet: quiet); try {
initialized = await pullAbImpl(quiet: quiet);
} catch (_) {}
abLoading.value = false; abLoading.value = false;
} }
@ -1257,12 +1251,12 @@ class Ab extends BaseAb {
Future<bool> pullAbImpl({quiet = false}) async { Future<bool> pullAbImpl({quiet = false}) async {
bool ret = true; bool ret = true;
List<Peer> tmpPeers = []; List<Peer> tmpPeers = [];
if (!await _fetchPeers(tmpPeers)) { if (!await _fetchPeers(tmpPeers, quiet: quiet)) {
ret = false; ret = false;
} }
peers.value = tmpPeers; peers.value = tmpPeers;
List<AbTag> tmpTags = []; List<AbTag> tmpTags = [];
if (!await _fetchTags(tmpTags)) { if (!await _fetchTags(tmpTags, quiet: quiet)) {
ret = false; ret = false;
} }
tags.value = tmpTags.map((e) => e.name).toList(); tags.value = tmpTags.map((e) => e.name).toList();
@ -1274,8 +1268,9 @@ class Ab extends BaseAb {
return ret; return ret;
} }
Future<bool> _fetchPeers(List<Peer> tmpPeers) async { Future<bool> _fetchPeers(List<Peer> tmpPeers, {quiet = false}) async {
final api = "${await bind.mainGetApiServer()}/api/ab/peers"; final api = "${await bind.mainGetApiServer()}/api/ab/peers";
int? statusCode;
try { try {
var uri0 = Uri.parse(api); var uri0 = Uri.parse(api);
final pageSize = 100; final pageSize = 100;
@ -1296,6 +1291,7 @@ class Ab extends BaseAb {
var headers = getHttpHeaders(); var headers = getHttpHeaders();
headers['Content-Type'] = "application/json"; headers['Content-Type'] = "application/json";
final resp = await http.post(uri, headers: headers); final resp = await http.post(uri, headers: headers);
statusCode = resp.statusCode;
Map<String, dynamic> json = Map<String, dynamic> json =
_jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode); _jsonDecodeRespMap(utf8.decode(resp.bodyBytes), resp.statusCode);
if (json.containsKey('error')) { if (json.containsKey('error')) {
@ -1324,13 +1320,23 @@ class Ab extends BaseAb {
} while (current * pageSize < total); } while (current * pageSize < total);
return true; return true;
} catch (err) { } catch (err) {
debugPrint('_fetchPeers err: ${err.toString()}'); if (!quiet) {
pullError.value =
'${translate('pull_ab_failed_tip')}: ${translate(err.toString())}';
}
} finally {
if (pullError.isNotEmpty) {
if (statusCode == 401) {
gFFI.userModel.reset(resetOther: true);
}
}
} }
return false; return false;
} }
Future<bool> _fetchTags(List<AbTag> tmpTags) async { Future<bool> _fetchTags(List<AbTag> tmpTags, {quiet = false}) async {
final api = "${await bind.mainGetApiServer()}/api/ab/tags/${profile.guid}"; final api = "${await bind.mainGetApiServer()}/api/ab/tags/${profile.guid}";
int? statusCode;
try { try {
var uri0 = Uri.parse(api); var uri0 = Uri.parse(api);
var uri = Uri( var uri = Uri(
@ -1342,6 +1348,7 @@ class Ab extends BaseAb {
var headers = getHttpHeaders(); var headers = getHttpHeaders();
headers['Content-Type'] = "application/json"; headers['Content-Type'] = "application/json";
final resp = await http.post(uri, headers: headers); final resp = await http.post(uri, headers: headers);
statusCode = resp.statusCode;
List<dynamic> json = List<dynamic> json =
_jsonDecodeRespList(utf8.decode(resp.bodyBytes), resp.statusCode); _jsonDecodeRespList(utf8.decode(resp.bodyBytes), resp.statusCode);
if (resp.statusCode != 200) { if (resp.statusCode != 200) {
@ -1359,7 +1366,16 @@ class Ab extends BaseAb {
} }
return true; return true;
} catch (err) { } catch (err) {
debugPrint('_fetchTags err: ${err.toString()}'); if (!quiet) {
pullError.value =
'${translate('pull_ab_failed_tip')}: ${translate(err.toString())}';
}
} finally {
if (pullError.isNotEmpty) {
if (statusCode == 401) {
gFFI.userModel.reset(resetOther: true);
}
}
} }
return false; return false;
} }