From b975061a6701a6d5ccfae519dd5e7f47e5e92f55 Mon Sep 17 00:00:00 2001
From: poka <poka@p2p.legal>
Date: Sat, 16 Jul 2022 21:03:40 +0200
Subject: [PATCH] change indexer endpoint in settings

---
 lib/main.dart                      |   8 +-
 lib/providers/duniter_indexer.dart |  98 +++++--
 lib/screens/home.dart              |   8 +-
 lib/screens/settings.dart          | 393 +++++++++++++++++++----------
 4 files changed, 339 insertions(+), 168 deletions(-)

diff --git a/lib/main.dart b/lib/main.dart
index 5517851f..148d336a 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -60,12 +60,16 @@ Future<void> main() async {
   }
 
   HomeProvider _homeProvider = HomeProvider();
-  DuniterIndexer _duniterIndexer = DuniterIndexer();
+  // DuniterIndexer _duniterIndexer = DuniterIndexer();
   await initHiveForFlutter();
   await _homeProvider.initHive();
   appVersion = await _homeProvider.getAppVersion();
   prefs = await SharedPreferences.getInstance();
 
+  // Reset GraphQL cache
+  final cache = HiveStore();
+  cache.reset();
+
   // Configure Hive and open boxes
   Hive.registerAdapter(WalletDataAdapter());
   Hive.registerAdapter(ChestDataAdapter());
@@ -85,8 +89,6 @@ Future<void> main() async {
   }
   // log.d(await configBox.get('endpoint'));
 
-  _duniterIndexer.getValidIndexerEndpoint();
-
   HttpOverrides.global = MyHttpOverrides();
 
   if (kReleaseMode && enableSentry) {
diff --git a/lib/providers/duniter_indexer.dart b/lib/providers/duniter_indexer.dart
index 26c8c5fe..62aadbee 100644
--- a/lib/providers/duniter_indexer.dart
+++ b/lib/providers/duniter_indexer.dart
@@ -22,41 +22,82 @@ class DuniterIndexer with ChangeNotifier {
   int nPage = 1;
   int nRepositories = 20;
   List? transBC;
+  List listIndexerEndpoints = [];
+  bool isLoadingIndexer = false;
 
   void reload() {
     notifyListeners();
   }
 
-  Future checkIndexerEndpoint() async {
-    final oldEndpoint = indexerEndpoint;
-    while (true) {
-      await Future.delayed(const Duration(seconds: 30));
-      final _client = HttpClient();
-      _client.connectionTimeout = const Duration(milliseconds: 4000);
-      try {
-        final request =
-            await _client.postUrl(Uri.parse('$oldEndpoint/v1/graphql'));
-        final response = await request.close();
-        if (response.statusCode != 200) {
-          log.d('INDEXER IS OFFILINE');
-          indexerEndpoint = '';
-        } else {
-          // log.d('Indexer is online');
-          indexerEndpoint = oldEndpoint;
-        }
-      } catch (e) {
+  Future<bool> checkIndexerEndpoint(String endpoint) async {
+    isLoadingIndexer = true;
+    notifyListeners();
+    final _client = HttpClient();
+    _client.connectionTimeout = const Duration(milliseconds: 4000);
+    try {
+      final request = await _client.postUrl(Uri.parse('$endpoint/v1/graphql'));
+      final response = await request.close();
+      if (response.statusCode != 200) {
         log.d('INDEXER IS OFFILINE');
         indexerEndpoint = '';
+        isLoadingIndexer = false;
+        notifyListeners();
+        return false;
+      } else {
+        indexerEndpoint = endpoint;
+        await configBox.put('indexerEndpoint', endpoint);
+        // await configBox.put('customEndpoint', endpoint);
+        isLoadingIndexer = false;
+        notifyListeners();
+        final cache = HiveStore();
+        cache.reset();
+        return true;
       }
+    } catch (e) {
+      log.d('INDEXER IS OFFILINE');
+      indexerEndpoint = '';
+      isLoadingIndexer = false;
+      notifyListeners();
+      return false;
     }
   }
 
+  // Future checkIndexerEndpointBackground() async {
+  //   final oldEndpoint = indexerEndpoint;
+  //   while (true) {
+  //     await Future.delayed(const Duration(seconds: 30));
+  //     final isValid = await checkIndexerEndpoint(oldEndpoint);
+  //     if (!isValid) {
+  //       log.d('INDEXER IS OFFILINE');
+  //       indexerEndpoint = '';
+  //     } else {
+  //       // log.d('Indexer is online');
+  //       indexerEndpoint = oldEndpoint;
+  //     }
+  //   }
+  // }
+
   Future<String> getValidIndexerEndpoint() async {
-    List _listEndpoints = await rootBundle
+    // await configBox.delete('indexerEndpoint');
+
+    listIndexerEndpoints = await rootBundle
         .loadString('config/indexer_endpoints.json')
         .then((jsonStr) => jsonDecode(jsonStr));
     // _listEndpoints.shuffle();
 
+    log.d(listIndexerEndpoints);
+
+    if (configBox.containsKey('customIndexer')) {
+      // return configBox.get('customIndexer');
+      listIndexerEndpoints.insert(0, configBox.get('customIndexer'));
+    }
+
+    if (configBox.containsKey('indexerEndpoint')) {
+      if (await checkIndexerEndpoint(configBox.get('indexerEndpoint'))) {
+        return configBox.get('indexerEndpoint');
+      }
+    }
+
     int i = 0;
     // String _endpoint = '';
     int _statusCode = 0;
@@ -65,25 +106,28 @@ class DuniterIndexer with ChangeNotifier {
     _client.connectionTimeout = const Duration(milliseconds: 3000);
 
     do {
-      int listLenght = _listEndpoints.length;
+      int listLenght = listIndexerEndpoints.length;
       if (i >= listLenght) {
         log.e('NO VALID INDEXER ENDPOINT FOUND');
         indexerEndpoint = '';
         break;
       }
-      log.d(
-          (i + 1).toString() + 'n indexer endpoint try: ${_listEndpoints[i]}');
+      log.d((i + 1).toString() +
+          'n indexer endpoint try: ${listIndexerEndpoints[i]}');
 
       if (i != 0) {
         await Future.delayed(const Duration(milliseconds: 300));
       }
 
       try {
-        final request =
-            await _client.postUrl(Uri.parse('${_listEndpoints[i]}/v1/graphql'));
+        String endpointPath = '${listIndexerEndpoints[i]}/v1/graphql';
+
+        final request = await _client.postUrl(Uri.parse(endpointPath));
         final response = await request.close();
 
-        indexerEndpoint = _listEndpoints[i];
+        indexerEndpoint = listIndexerEndpoints[i];
+        await configBox.put('indexerEndpoint', listIndexerEndpoints[i]);
+
         _statusCode = response.statusCode;
         i++;
       } on TimeoutException catch (_) {
@@ -202,13 +246,15 @@ class DuniterIndexer with ChangeNotifier {
       return const Text('Aucun résultat');
     }
 
+    log.d(indexerEndpoint);
     final _httpLink = HttpLink(
       '$indexerEndpoint/v1/graphql',
     );
 
     final _client = ValueNotifier(
       GraphQLClient(
-        cache: GraphQLCache(store: HiveStore()),
+        cache: GraphQLCache(
+            store: HiveStore()), // GraphQLCache(store: HiveStore())
         link: _httpLink,
       ),
     );
diff --git a/lib/screens/home.dart b/lib/screens/home.dart
index 58468b37..26c75f4f 100644
--- a/lib/screens/home.dart
+++ b/lib/screens/home.dart
@@ -107,6 +107,10 @@ class HomeScreen extends StatelessWidget {
         builder: (ctx) => StatefulWrapper(
             onInit: () {
               WidgetsBinding.instance.addPostFrameCallback((_) async {
+                DuniterIndexer _duniterIndexer =
+                    Provider.of<DuniterIndexer>(ctx, listen: false);
+                _duniterIndexer.getValidIndexerEndpoint();
+
                 if (!_sub.sdkReady && !_sub.sdkLoading) await _sub.initApi();
                 if (_sub.sdkReady && !_sub.nodeConnected) {
                   // Check if versionData non compatible, drop everything
@@ -147,9 +151,7 @@ class HomeScreen extends StatelessWidget {
                     }
                   });
                 }
-                DuniterIndexer _duniterIndexer =
-                    Provider.of<DuniterIndexer>(ctx, listen: false);
-                _duniterIndexer.checkIndexerEndpoint();
+                // _duniterIndexer.checkIndexerEndpointBackground();
               });
             },
             child: isWalletsExists ? geckHome(context) : welcomeHome(context)
diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart
index 83f6fb59..80ff60ea 100644
--- a/lib/screens/settings.dart
+++ b/lib/screens/settings.dart
@@ -1,13 +1,14 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
+import 'package:gecko/providers/duniter_indexer.dart';
 import 'package:gecko/providers/my_wallets.dart';
 import 'package:gecko/providers/settings_provider.dart';
 import 'package:gecko/providers/substrate_sdk.dart';
 import 'package:gecko/globals.dart';
 import 'package:polkawallet_sdk/api/types/networkParams.dart';
 import 'package:provider/provider.dart';
-import 'package:dropdown_button2/dropdown_button2.dart';
+// import 'package:dropdown_button2/dropdown_button2.dart';
 
 // ignore: must_be_immutable
 class SettingsScreen extends StatelessWidget {
@@ -15,18 +16,61 @@ class SettingsScreen extends StatelessWidget {
 
   SettingsScreen({Key? key}) : super(key: key);
 
-  // Initial Selected Value
-  String? selectedDuniterEndpoint;
-
   @override
   Widget build(BuildContext context) {
     SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
-    SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
 
     const double buttonHigh = 50;
     const double buttonWidth = 240;
     const double fontSize = 16;
 
+    return Scaffold(
+      backgroundColor: backgroundColor,
+      appBar: AppBar(
+          toolbarHeight: 60 * ratio,
+          title: SizedBox(
+            height: 22,
+            child: Text('parameters'.tr()),
+          )),
+      body: Column(children: <Widget>[
+        const SizedBox(height: 30),
+        duniterEndpointSelection(context),
+        const SizedBox(height: 50),
+        indexerEndpointSelection(context),
+
+        // SizedBox(height: isTall ? 80 : 120),
+        const Spacer(),
+        SizedBox(
+          height: buttonHigh,
+          width: buttonWidth,
+          child: Center(
+            child: InkWell(
+              key: const Key('deleteChest'),
+              onTap: () async {
+                log.i('Oublier tous mes coffres');
+                await _myWallets.deleteAllWallet(context);
+              },
+              child: Text(
+                'forgetAllMyChests'.tr(),
+                style: const TextStyle(
+                  fontSize: fontSize + 4,
+                  color: Color(0xffD80000),
+                  fontWeight: FontWeight.w600,
+                ),
+              ),
+            ),
+          ),
+        ),
+        // const Spacer(),
+        SizedBox(height: isTall ? 90 : 60),
+      ]),
+    );
+  }
+
+  Widget duniterEndpointSelection(BuildContext context) {
+    SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
+    String? selectedDuniterEndpoint;
+
     // List of items in our dropdown menu
     var duniterBootstrapNodes = _sub.getDuniterBootstrap();
     selectedDuniterEndpoint =
@@ -56,147 +100,224 @@ class SettingsScreen extends StatelessWidget {
             ? configBox.get('customEndpoint')
             : 'wss://');
 
-    return Scaffold(
-      backgroundColor: backgroundColor,
-      appBar: AppBar(
-          toolbarHeight: 60 * ratio,
-          title: SizedBox(
-            height: 22,
-            child: Text('parameters'.tr()),
-          )),
-      body: Column(children: <Widget>[
-        const SizedBox(height: 60),
-
-        Row(children: [
-          Consumer<SubstrateSdk>(builder: (context, _sub, _) {
-            log.d(_sub.sdk.api.connectedNode?.endpoint);
-            return Expanded(
-              child: Row(children: [
-                const SizedBox(width: 10),
-                Text('currencyNode'.tr(args: [currencyName])),
-                const Spacer(),
-                Icon(_sub.nodeConnected && !_sub.isLoadingEndpoint
-                    ? Icons.check
-                    : Icons.close),
-                const Spacer(),
-                Consumer<SettingsProvider>(builder: (context, _set, _) {
-                  return DropdownButtonHideUnderline(
-                    child: DropdownButton(
-                      // alignment: AlignmentDirectional.topStart,
-                      value: selectedDuniterEndpoint,
-                      icon: const Icon(Icons.keyboard_arrow_down),
-                      items: duniterBootstrapNodes
-                          .map((NetworkParams _endpointParams) {
-                        return DropdownMenuItem(
-                          value: _endpointParams.endpoint,
-                          child: Text(_endpointParams.endpoint!),
-                        );
-                      }).toList(),
-                      onChanged: (String? _newEndpoint) {
-                        log.d(_newEndpoint!);
-                        selectedDuniterEndpoint = _newEndpoint;
-                        _set.reload();
-                      },
-                    ),
-                  );
-                }),
-                const Spacer(flex: 5),
-                _sub.isLoadingEndpoint
-                    ? CircularProgressIndicator(color: orangeC)
-                    : Consumer<SettingsProvider>(builder: (context, _set, _) {
-                        return IconButton(
-                            icon: Icon(
-                              Icons.send,
-                              color: selectedDuniterEndpoint !=
-                                      _sub.getConnectedEndpoint()
-                                  ? orangeC
-                                  : Colors.grey[500],
-                              size: 40,
-                            ),
-                            onPressed: selectedDuniterEndpoint !=
+    return Column(children: <Widget>[
+      Row(children: [
+        Consumer<SubstrateSdk>(builder: (context, _sub, _) {
+          log.d(_sub.sdk.api.connectedNode?.endpoint);
+          return Expanded(
+            child: Row(children: [
+              const SizedBox(width: 10),
+              Text('currencyNode'.tr(args: [currencyName])),
+              const Spacer(),
+              Icon(_sub.nodeConnected && !_sub.isLoadingEndpoint
+                  ? Icons.check
+                  : Icons.close),
+              const Spacer(),
+              Consumer<SettingsProvider>(builder: (context, _set, _) {
+                return DropdownButtonHideUnderline(
+                  child: DropdownButton(
+                    // alignment: AlignmentDirectional.topStart,
+                    value: selectedDuniterEndpoint,
+                    icon: const Icon(Icons.keyboard_arrow_down),
+                    items: duniterBootstrapNodes
+                        .map((NetworkParams _endpointParams) {
+                      return DropdownMenuItem(
+                        value: _endpointParams.endpoint,
+                        child: Text(_endpointParams.endpoint!),
+                      );
+                    }).toList(),
+                    onChanged: (String? _newEndpoint) {
+                      log.d(_newEndpoint!);
+                      selectedDuniterEndpoint = _newEndpoint;
+                      _set.reload();
+                    },
+                  ),
+                );
+              }),
+              const Spacer(flex: 5),
+              _sub.isLoadingEndpoint
+                  ? CircularProgressIndicator(color: orangeC)
+                  : Consumer<SettingsProvider>(builder: (context, _set, _) {
+                      return IconButton(
+                          icon: Icon(
+                            Icons.send,
+                            color: selectedDuniterEndpoint !=
                                     _sub.getConnectedEndpoint()
-                                ? () async {
-                                    if (selectedDuniterEndpoint == 'Auto') {
-                                      configBox.delete('customEndpoint');
-                                      configBox.put('autoEndpoint', true);
-                                    } else {
-                                      configBox.put('autoEndpoint', false);
-                                      final finalEndpoint =
-                                          selectedDuniterEndpoint ==
-                                                  'Personnalisé'
-                                              ? _endpointController.text
-                                              : selectedDuniterEndpoint;
-                                      configBox.put(
-                                          'customEndpoint', finalEndpoint);
-                                    }
-                                    await _sub.connectNode(context);
+                                ? orangeC
+                                : Colors.grey[500],
+                            size: 40,
+                          ),
+                          onPressed: selectedDuniterEndpoint !=
+                                  _sub.getConnectedEndpoint()
+                              ? () async {
+                                  if (selectedDuniterEndpoint == 'Auto') {
+                                    configBox.delete('customEndpoint');
+                                    configBox.put('autoEndpoint', true);
+                                  } else {
+                                    configBox.put('autoEndpoint', false);
+                                    final finalEndpoint =
+                                        selectedDuniterEndpoint ==
+                                                'Personnalisé'
+                                            ? _endpointController.text
+                                            : selectedDuniterEndpoint;
+                                    configBox.put(
+                                        'customEndpoint', finalEndpoint);
                                   }
-                                : null);
-                      }),
-                const Spacer(flex: 8),
-              ]),
-            );
-          }),
-        ]),
-        Consumer<SettingsProvider>(builder: (context, _set, _) {
+                                  await _sub.connectNode(context);
+                                }
+                              : null);
+                    }),
+              const Spacer(flex: 8),
+            ]),
+          );
+        }),
+      ]),
+      Consumer<SettingsProvider>(builder: (context, _set, _) {
+        return Visibility(
+          visible: selectedDuniterEndpoint == 'Personnalisé',
+          child: SizedBox(
+            width: 200,
+            height: 50,
+            child: TextField(
+              controller: _endpointController,
+              autocorrect: false,
+            ),
+          ),
+        );
+      }),
+      Consumer<SubstrateSdk>(builder: (context, _sub, _) {
+        return Consumer<SettingsProvider>(builder: (context, _set, _) {
           return Visibility(
-            visible: selectedDuniterEndpoint == 'Personnalisé',
+            visible: selectedDuniterEndpoint == 'Auto',
             child: SizedBox(
-              width: 200,
-              height: 50,
-              child: TextField(
-                controller: _endpointController,
-                autocorrect: false,
+              width: 250,
+              height: _sub.getConnectedEndpoint() == null ? 60 : 20,
+              child: Text(
+                _sub.getConnectedEndpoint() ??
+                    "Un noeud sûr et valide sera choisi automatiquement parmis une liste aléatoire.",
+                style: TextStyle(
+                    fontSize: 15,
+                    fontStyle: FontStyle.italic,
+                    color: Colors.grey[700]),
               ),
             ),
           );
-        }),
-        Consumer<SubstrateSdk>(builder: (context, _sub, _) {
-          return Consumer<SettingsProvider>(builder: (context, _set, _) {
-            return Visibility(
-              visible: selectedDuniterEndpoint == 'Auto',
-              child: SizedBox(
-                width: 250,
-                height: 60,
-                child: Text(
-                  _sub.getConnectedEndpoint() ??
-                      "Un noeud sûr et valide sera choisi automatiquement parmis une liste aléatoire.",
-                  style: TextStyle(
-                      fontSize: 15,
-                      fontStyle: FontStyle.italic,
-                      color: Colors.grey[700]),
-                ),
-              ),
-            );
-          });
-        }),
+        });
+      }),
+    ]);
+  }
 
-        // SizedBox(height: isTall ? 80 : 120),
-        const Spacer(),
-        SizedBox(
-          height: buttonHigh,
-          width: buttonWidth,
-          child: Center(
-            child: InkWell(
-              key: const Key('deleteChest'),
-              onTap: () async {
-                log.i('Oublier tous mes coffres');
-                await _myWallets.deleteAllWallet(context);
-              },
+  Widget indexerEndpointSelection(BuildContext context) {
+    DuniterIndexer _indexer =
+        Provider.of<DuniterIndexer>(context, listen: false);
+
+    String? selectedIndexerEndpoint;
+    if (configBox.containsKey('customIndexer')) {
+      selectedIndexerEndpoint = '';
+    } else {
+      selectedIndexerEndpoint = indexerEndpoint;
+    }
+
+    if (selectedIndexerEndpoint == '') {
+      selectedIndexerEndpoint = _indexer.listIndexerEndpoints[0];
+    }
+
+    TextEditingController _endpointController = TextEditingController(
+        text: configBox.containsKey('customIndexer')
+            ? configBox.get('customIndexer')
+            : 'https://');
+
+    return Column(children: <Widget>[
+      Row(children: [
+        Consumer<DuniterIndexer>(builder: (context, _indexer, _) {
+          log.d(selectedIndexerEndpoint);
+          log.d(_indexer.listIndexerEndpoints);
+          return Expanded(
+            child: Row(children: [
+              const SizedBox(width: 10),
+              const Text('Indexer : '),
+              const Spacer(),
+              Icon(indexerEndpoint != '' ? Icons.check : Icons.close),
+              const Spacer(),
+              Consumer<SettingsProvider>(builder: (context, _set, _) {
+                return DropdownButtonHideUnderline(
+                  child: DropdownButton(
+                    // alignment: AlignmentDirectional.topStart,
+                    value: selectedIndexerEndpoint,
+                    icon: const Icon(Icons.keyboard_arrow_down),
+                    items:
+                        _indexer.listIndexerEndpoints.map((_indexerEndpoint) {
+                      return DropdownMenuItem(
+                        value: _indexerEndpoint,
+                        child: Text(_indexerEndpoint),
+                      );
+                    }).toList(),
+                    onChanged: (_newEndpoint) {
+                      log.d(_newEndpoint!);
+                      selectedIndexerEndpoint = _newEndpoint.toString();
+                      _set.reload();
+                    },
+                  ),
+                );
+              }),
+              const Spacer(flex: 5),
+              _indexer.isLoadingIndexer
+                  ? CircularProgressIndicator(color: orangeC)
+                  : Consumer<SettingsProvider>(builder: (context, _set, _) {
+                      return IconButton(
+                          icon: Icon(
+                            Icons.send,
+                            color: selectedIndexerEndpoint != indexerEndpoint
+                                ? orangeC
+                                : Colors.grey[500],
+                            size: 40,
+                          ),
+                          onPressed: selectedIndexerEndpoint != indexerEndpoint
+                              ? () async {
+                                  log.d(
+                                      'connection to indexer $selectedIndexerEndpoint');
+                                  await _indexer.checkIndexerEndpoint(
+                                      selectedIndexerEndpoint!);
+                                }
+                              : null);
+                    }),
+              const Spacer(flex: 8),
+            ]),
+          );
+        }),
+      ]),
+      Consumer<SettingsProvider>(builder: (context, _set, _) {
+        return Visibility(
+          visible: selectedIndexerEndpoint == 'Personnalisé',
+          child: SizedBox(
+            width: 200,
+            height: 50,
+            child: TextField(
+              controller: _endpointController,
+              autocorrect: false,
+            ),
+          ),
+        );
+      }),
+      Consumer<SubstrateSdk>(builder: (context, _sub, _) {
+        return Consumer<SettingsProvider>(builder: (context, _set, _) {
+          return Visibility(
+            visible: selectedIndexerEndpoint == 'Auto',
+            child: SizedBox(
+              width: 250,
+              height: 60,
               child: Text(
-                'forgetAllMyChests'.tr(),
-                style: const TextStyle(
-                  fontSize: fontSize + 4,
-                  color: Color(0xffD80000),
-                  fontWeight: FontWeight.w600,
-                ),
+                _sub.getConnectedEndpoint() ??
+                    "Un noeud sûr et valide sera choisi automatiquement parmis une liste aléatoire.",
+                style: TextStyle(
+                    fontSize: 15,
+                    fontStyle: FontStyle.italic,
+                    color: Colors.grey[700]),
               ),
             ),
-          ),
-        ),
-        // const Spacer(),
-        SizedBox(height: isTall ? 90 : 60),
-      ]),
-    );
+          );
+        });
+      }),
+    ]);
   }
 }
-- 
GitLab