change indexer endpoint in settings

This commit is contained in:
poka 2022-07-16 21:03:40 +02:00
parent 0f43100969
commit b975061a67
4 changed files with 340 additions and 169 deletions

View File

@ -60,12 +60,16 @@ Future<void> main() async {
} }
HomeProvider _homeProvider = HomeProvider(); HomeProvider _homeProvider = HomeProvider();
DuniterIndexer _duniterIndexer = DuniterIndexer(); // DuniterIndexer _duniterIndexer = DuniterIndexer();
await initHiveForFlutter(); await initHiveForFlutter();
await _homeProvider.initHive(); await _homeProvider.initHive();
appVersion = await _homeProvider.getAppVersion(); appVersion = await _homeProvider.getAppVersion();
prefs = await SharedPreferences.getInstance(); prefs = await SharedPreferences.getInstance();
// Reset GraphQL cache
final cache = HiveStore();
cache.reset();
// Configure Hive and open boxes // Configure Hive and open boxes
Hive.registerAdapter(WalletDataAdapter()); Hive.registerAdapter(WalletDataAdapter());
Hive.registerAdapter(ChestDataAdapter()); Hive.registerAdapter(ChestDataAdapter());
@ -85,8 +89,6 @@ Future<void> main() async {
} }
// log.d(await configBox.get('endpoint')); // log.d(await configBox.get('endpoint'));
_duniterIndexer.getValidIndexerEndpoint();
HttpOverrides.global = MyHttpOverrides(); HttpOverrides.global = MyHttpOverrides();
if (kReleaseMode && enableSentry) { if (kReleaseMode && enableSentry) {

View File

@ -22,41 +22,82 @@ class DuniterIndexer with ChangeNotifier {
int nPage = 1; int nPage = 1;
int nRepositories = 20; int nRepositories = 20;
List? transBC; List? transBC;
List listIndexerEndpoints = [];
bool isLoadingIndexer = false;
void reload() { void reload() {
notifyListeners(); notifyListeners();
} }
Future checkIndexerEndpoint() async { Future<bool> checkIndexerEndpoint(String endpoint) async {
final oldEndpoint = indexerEndpoint; isLoadingIndexer = true;
while (true) { notifyListeners();
await Future.delayed(const Duration(seconds: 30)); final _client = HttpClient();
final _client = HttpClient(); _client.connectionTimeout = const Duration(milliseconds: 4000);
_client.connectionTimeout = const Duration(milliseconds: 4000); try {
try { final request = await _client.postUrl(Uri.parse('$endpoint/v1/graphql'));
final request = final response = await request.close();
await _client.postUrl(Uri.parse('$oldEndpoint/v1/graphql')); if (response.statusCode != 200) {
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) {
log.d('INDEXER IS OFFILINE'); log.d('INDEXER IS OFFILINE');
indexerEndpoint = ''; 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 { Future<String> getValidIndexerEndpoint() async {
List _listEndpoints = await rootBundle // await configBox.delete('indexerEndpoint');
listIndexerEndpoints = await rootBundle
.loadString('config/indexer_endpoints.json') .loadString('config/indexer_endpoints.json')
.then((jsonStr) => jsonDecode(jsonStr)); .then((jsonStr) => jsonDecode(jsonStr));
// _listEndpoints.shuffle(); // _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; int i = 0;
// String _endpoint = ''; // String _endpoint = '';
int _statusCode = 0; int _statusCode = 0;
@ -65,25 +106,28 @@ class DuniterIndexer with ChangeNotifier {
_client.connectionTimeout = const Duration(milliseconds: 3000); _client.connectionTimeout = const Duration(milliseconds: 3000);
do { do {
int listLenght = _listEndpoints.length; int listLenght = listIndexerEndpoints.length;
if (i >= listLenght) { if (i >= listLenght) {
log.e('NO VALID INDEXER ENDPOINT FOUND'); log.e('NO VALID INDEXER ENDPOINT FOUND');
indexerEndpoint = ''; indexerEndpoint = '';
break; break;
} }
log.d( log.d((i + 1).toString() +
(i + 1).toString() + 'n indexer endpoint try: ${_listEndpoints[i]}'); 'n indexer endpoint try: ${listIndexerEndpoints[i]}');
if (i != 0) { if (i != 0) {
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
} }
try { try {
final request = String endpointPath = '${listIndexerEndpoints[i]}/v1/graphql';
await _client.postUrl(Uri.parse('${_listEndpoints[i]}/v1/graphql'));
final request = await _client.postUrl(Uri.parse(endpointPath));
final response = await request.close(); final response = await request.close();
indexerEndpoint = _listEndpoints[i]; indexerEndpoint = listIndexerEndpoints[i];
await configBox.put('indexerEndpoint', listIndexerEndpoints[i]);
_statusCode = response.statusCode; _statusCode = response.statusCode;
i++; i++;
} on TimeoutException catch (_) { } on TimeoutException catch (_) {
@ -202,13 +246,15 @@ class DuniterIndexer with ChangeNotifier {
return const Text('Aucun résultat'); return const Text('Aucun résultat');
} }
log.d(indexerEndpoint);
final _httpLink = HttpLink( final _httpLink = HttpLink(
'$indexerEndpoint/v1/graphql', '$indexerEndpoint/v1/graphql',
); );
final _client = ValueNotifier( final _client = ValueNotifier(
GraphQLClient( GraphQLClient(
cache: GraphQLCache(store: HiveStore()), cache: GraphQLCache(
store: HiveStore()), // GraphQLCache(store: HiveStore())
link: _httpLink, link: _httpLink,
), ),
); );

View File

@ -107,6 +107,10 @@ class HomeScreen extends StatelessWidget {
builder: (ctx) => StatefulWrapper( builder: (ctx) => StatefulWrapper(
onInit: () { onInit: () {
WidgetsBinding.instance.addPostFrameCallback((_) async { 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.sdkLoading) await _sub.initApi();
if (_sub.sdkReady && !_sub.nodeConnected) { if (_sub.sdkReady && !_sub.nodeConnected) {
// Check if versionData non compatible, drop everything // Check if versionData non compatible, drop everything
@ -147,9 +151,7 @@ class HomeScreen extends StatelessWidget {
} }
}); });
} }
DuniterIndexer _duniterIndexer = // _duniterIndexer.checkIndexerEndpointBackground();
Provider.of<DuniterIndexer>(ctx, listen: false);
_duniterIndexer.checkIndexerEndpoint();
}); });
}, },
child: isWalletsExists ? geckHome(context) : welcomeHome(context) child: isWalletsExists ? geckHome(context) : welcomeHome(context)

View File

@ -1,13 +1,14 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:gecko/providers/duniter_indexer.dart';
import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/my_wallets.dart';
import 'package:gecko/providers/settings_provider.dart'; import 'package:gecko/providers/settings_provider.dart';
import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/substrate_sdk.dart';
import 'package:gecko/globals.dart'; import 'package:gecko/globals.dart';
import 'package:polkawallet_sdk/api/types/networkParams.dart'; import 'package:polkawallet_sdk/api/types/networkParams.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:dropdown_button2/dropdown_button2.dart'; // import 'package:dropdown_button2/dropdown_button2.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class SettingsScreen extends StatelessWidget { class SettingsScreen extends StatelessWidget {
@ -15,18 +16,61 @@ class SettingsScreen extends StatelessWidget {
SettingsScreen({Key? key}) : super(key: key); SettingsScreen({Key? key}) : super(key: key);
// Initial Selected Value
String? selectedDuniterEndpoint;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
SubstrateSdk _sub = Provider.of<SubstrateSdk>(context, listen: false);
const double buttonHigh = 50; const double buttonHigh = 50;
const double buttonWidth = 240; const double buttonWidth = 240;
const double fontSize = 16; 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 // List of items in our dropdown menu
var duniterBootstrapNodes = _sub.getDuniterBootstrap(); var duniterBootstrapNodes = _sub.getDuniterBootstrap();
selectedDuniterEndpoint = selectedDuniterEndpoint =
@ -56,147 +100,224 @@ class SettingsScreen extends StatelessWidget {
? configBox.get('customEndpoint') ? configBox.get('customEndpoint')
: 'wss://'); : 'wss://');
return Scaffold( return Column(children: <Widget>[
backgroundColor: backgroundColor, Row(children: [
appBar: AppBar( Consumer<SubstrateSdk>(builder: (context, _sub, _) {
toolbarHeight: 60 * ratio, log.d(_sub.sdk.api.connectedNode?.endpoint);
title: SizedBox( return Expanded(
height: 22, child: Row(children: [
child: Text('parameters'.tr()), const SizedBox(width: 10),
)), Text('currencyNode'.tr(args: [currencyName])),
body: Column(children: <Widget>[ const Spacer(),
const SizedBox(height: 60), Icon(_sub.nodeConnected && !_sub.isLoadingEndpoint
? Icons.check
Row(children: [ : Icons.close),
Consumer<SubstrateSdk>(builder: (context, _sub, _) { const Spacer(),
log.d(_sub.sdk.api.connectedNode?.endpoint); Consumer<SettingsProvider>(builder: (context, _set, _) {
return Expanded( return DropdownButtonHideUnderline(
child: Row(children: [ child: DropdownButton(
const SizedBox(width: 10), // alignment: AlignmentDirectional.topStart,
Text('currencyNode'.tr(args: [currencyName])), value: selectedDuniterEndpoint,
const Spacer(), icon: const Icon(Icons.keyboard_arrow_down),
Icon(_sub.nodeConnected && !_sub.isLoadingEndpoint items: duniterBootstrapNodes
? Icons.check .map((NetworkParams _endpointParams) {
: Icons.close), return DropdownMenuItem(
const Spacer(), value: _endpointParams.endpoint,
Consumer<SettingsProvider>(builder: (context, _set, _) { child: Text(_endpointParams.endpoint!),
return DropdownButtonHideUnderline( );
child: DropdownButton( }).toList(),
// alignment: AlignmentDirectional.topStart, onChanged: (String? _newEndpoint) {
value: selectedDuniterEndpoint, log.d(_newEndpoint!);
icon: const Icon(Icons.keyboard_arrow_down), selectedDuniterEndpoint = _newEndpoint;
items: duniterBootstrapNodes _set.reload();
.map((NetworkParams _endpointParams) { },
return DropdownMenuItem( ),
value: _endpointParams.endpoint, );
child: Text(_endpointParams.endpoint!), }),
); const Spacer(flex: 5),
}).toList(), _sub.isLoadingEndpoint
onChanged: (String? _newEndpoint) { ? CircularProgressIndicator(color: orangeC)
log.d(_newEndpoint!); : Consumer<SettingsProvider>(builder: (context, _set, _) {
selectedDuniterEndpoint = _newEndpoint; return IconButton(
_set.reload(); icon: Icon(
}, Icons.send,
), color: selectedDuniterEndpoint !=
);
}),
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 !=
_sub.getConnectedEndpoint() _sub.getConnectedEndpoint()
? () async { ? orangeC
if (selectedDuniterEndpoint == 'Auto') { : Colors.grey[500],
configBox.delete('customEndpoint'); size: 40,
configBox.put('autoEndpoint', true); ),
} else { onPressed: selectedDuniterEndpoint !=
configBox.put('autoEndpoint', false); _sub.getConnectedEndpoint()
final finalEndpoint = ? () async {
selectedDuniterEndpoint == if (selectedDuniterEndpoint == 'Auto') {
'Personnalisé' configBox.delete('customEndpoint');
? _endpointController.text configBox.put('autoEndpoint', true);
: selectedDuniterEndpoint; } else {
configBox.put( configBox.put('autoEndpoint', false);
'customEndpoint', finalEndpoint); final finalEndpoint =
} selectedDuniterEndpoint ==
await _sub.connectNode(context); 'Personnalisé'
? _endpointController.text
: selectedDuniterEndpoint;
configBox.put(
'customEndpoint', finalEndpoint);
} }
: null); await _sub.connectNode(context);
}), }
const Spacer(flex: 8), : null);
]), }),
); const Spacer(flex: 8),
}), ]),
]), );
Consumer<SettingsProvider>(builder: (context, _set, _) { }),
]),
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( return Visibility(
visible: selectedDuniterEndpoint == 'Personnalisé', visible: selectedDuniterEndpoint == 'Auto',
child: SizedBox( child: SizedBox(
width: 200, width: 250,
height: 50, height: _sub.getConnectedEndpoint() == null ? 60 : 20,
child: TextField( child: Text(
controller: _endpointController, _sub.getConnectedEndpoint() ??
autocorrect: false, "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), Widget indexerEndpointSelection(BuildContext context) {
const Spacer(), DuniterIndexer _indexer =
SizedBox( Provider.of<DuniterIndexer>(context, listen: false);
height: buttonHigh,
width: buttonWidth, String? selectedIndexerEndpoint;
child: Center( if (configBox.containsKey('customIndexer')) {
child: InkWell( selectedIndexerEndpoint = '';
key: const Key('deleteChest'), } else {
onTap: () async { selectedIndexerEndpoint = indexerEndpoint;
log.i('Oublier tous mes coffres'); }
await _myWallets.deleteAllWallet(context);
}, if (selectedIndexerEndpoint == '') {
child: Text( selectedIndexerEndpoint = _indexer.listIndexerEndpoints[0];
'forgetAllMyChests'.tr(), }
style: const TextStyle(
fontSize: fontSize + 4, TextEditingController _endpointController = TextEditingController(
color: Color(0xffD80000), text: configBox.containsKey('customIndexer')
fontWeight: FontWeight.w600, ? 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,
), ),
), ),
), );
// const Spacer(), }),
SizedBox(height: isTall ? 90 : 60), Consumer<SubstrateSdk>(builder: (context, _sub, _) {
]), return Consumer<SettingsProvider>(builder: (context, _set, _) {
); return Visibility(
visible: selectedIndexerEndpoint == '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]),
),
),
);
});
}),
]);
} }
} }