import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/queries_indexer.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/wallet_options.dart'; import 'package:gecko/providers/wallets_profiles.dart'; import 'package:gecko/screens/wallet_view.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:provider/provider.dart'; class DuniterIndexer with ChangeNotifier { Map walletNameIndexer = {}; String? fetchMoreCursor; Map? pageInfo; int nPage = 1; int nRepositories = 20; List? transBC; 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: 1000); 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) { log.d('INDEXER IS OFFILINE'); indexerEndpoint = ''; } } } Future getValidIndexerEndpoint() async { List _listEndpoints = await rootBundle .loadString('config/indexer_endpoints.json') .then((jsonStr) => jsonDecode(jsonStr)); // _listEndpoints.shuffle(); int i = 0; // String _endpoint = ''; int _statusCode = 0; final _client = HttpClient(); _client.connectionTimeout = const Duration(milliseconds: 1000); do { int listLenght = _listEndpoints.length; if (i >= listLenght) { log.e('NO VALID INDEXER ENDPOINT FOUND'); indexerEndpoint = ''; break; } log.d( (i + 1).toString() + 'n indexer endpoint try: ${_listEndpoints[i]}'); if (i != 0) { await Future.delayed(const Duration(milliseconds: 300)); } try { final request = await _client.postUrl(Uri.parse('${_listEndpoints[i]}/v1/graphql')); final response = await request.close(); indexerEndpoint = _listEndpoints[i]; _statusCode = response.statusCode; i++; } on TimeoutException catch (_) { log.e('This endpoint is timeout, next'); _statusCode = 50; i++; continue; } on SocketException catch (_) { log.e('This endpoint is a bad endpoint, next'); _statusCode = 70; i++; continue; } on Exception { log.e('Unknown error'); _statusCode = 60; i++; continue; } } while (_statusCode != 200); log.i('INDEXER: ' + indexerEndpoint); return indexerEndpoint; } Widget getNameByAddress(BuildContext context, String address, [WalletData? wallet, double size = 20, bool canEdit = false, Color _color = Colors.black, FontWeight fontWeight = FontWeight.w400, FontStyle fontStyle = FontStyle.italic]) { WalletOptionsProvider _walletOptions = Provider.of(context, listen: false); if (indexerEndpoint == '') { if (wallet == null) { return const SizedBox(); } else { if (canEdit) { return _walletOptions.walletName(context, wallet, size, _color); } else { return _walletOptions.walletNameController(context, wallet, size); } } } final _httpLink = HttpLink( '$indexerEndpoint/v1/graphql', ); final _client = ValueNotifier( GraphQLClient( cache: GraphQLCache(store: HiveStore()), link: _httpLink, ), ); return GraphQLProvider( client: _client, child: Query( options: QueryOptions( document: gql( getNameByAddressQ), // this is the query string you just created variables: { 'address': address, }, // pollInterval: const Duration(seconds: 10), ), builder: (QueryResult result, {VoidCallback? refetch, FetchMore? fetchMore}) { if (result.hasException) { return Text(result.exception.toString()); } if (result.isLoading) { return const Text('Loading'); } walletNameIndexer[address] = result.data?['account_by_pk']?['identity']?['name']; if (walletNameIndexer[address] == null) { if (wallet == null) { return const SizedBox(); } else { if (canEdit) { return _walletOptions.walletName( context, wallet, size, _color); } else { return _walletOptions.walletNameController( context, wallet, size); } } } return Text( _color == Colors.grey[700]! ? '(${walletNameIndexer[address]!})' : walletNameIndexer[address]!, style: TextStyle( fontSize: size, color: _color, fontWeight: fontWeight, fontStyle: fontStyle, ), ); }), ); } Widget searchIdentity(BuildContext context, String name) { // WalletOptionsProvider _walletOptions = // Provider.of(context, listen: false); CesiumPlusProvider _cesiumPlusProvider = Provider.of(context, listen: false); WalletsProfilesProvider _walletsProfiles = Provider.of(context, listen: false); if (indexerEndpoint == '') { return const Text('Aucun résultat'); } final _httpLink = HttpLink( '$indexerEndpoint/v1/graphql', ); final _client = ValueNotifier( GraphQLClient( cache: GraphQLCache(store: HiveStore()), link: _httpLink, ), ); return GraphQLProvider( client: _client, child: Query( options: QueryOptions( document: gql( searchAddressByNameQ), // this is the query string you just created variables: { 'name': name, }, // pollInterval: const Duration(seconds: 10), ), builder: (QueryResult result, {VoidCallback? refetch, FetchMore? fetchMore}) { if (result.hasException) { return Text(result.exception.toString()); } if (result.isLoading) { return Text('loading'.tr()); } final List identities = result.data?['search_identity'] ?? []; if (identities.isEmpty) { return Text('noResult'.tr()); } int keyID = 0; double _avatarSize = 55; return Expanded( child: ListView(children: [ for (Map profile in identities) Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: ListTile( key: Key('searchResult${keyID++}'), horizontalTitleGap: 40, contentPadding: const EdgeInsets.all(5), leading: _cesiumPlusProvider.defaultAvatar(_avatarSize), title: Row(children: [ Text(getShortPubkey(profile['id']), style: const TextStyle( fontSize: 18, fontFamily: 'Monospace', fontWeight: FontWeight.w500), textAlign: TextAlign.center), ]), trailing: Column( mainAxisAlignment: MainAxisAlignment.center, children: [balance(context, profile['id'], 16)]), subtitle: Row(children: [ Text(profile['name'] ?? '', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w500), textAlign: TextAlign.center), ]), dense: false, isThreeLine: false, onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) { _walletsProfiles.address = profile['id']; return WalletViewScreen( pubkey: profile['id'], username: g1WalletsBox .get(profile['id']) ?.id ?.username, avatar: g1WalletsBox.get(profile['id'])?.avatar, ); }), ); }), ), ]), ); }), ); } List parseHistory(blockchainTX, _pubkey) { var transBC = []; int i = 0; for (final trans in blockchainTX) { final transaction = trans['node']; final direction = transaction['issuer_id'] != _pubkey ? 'RECEIVED' : 'SENT'; transBC.add(i); transBC[i] = []; transBC[i].add(DateTime.parse(transaction['created_at'])); final int amountBrut = transaction['amount']; final num amount = removeDecimalZero(amountBrut / 100); if (direction == "RECEIVED") { transBC[i].add(transaction['issuer_id']); transBC[i].add(transaction['issuer']['identity']?['name'] ?? ''); transBC[i].add(amount.toString()); } else if (direction == "SENT") { transBC[i].add(transaction['receiver_id']); transBC[i].add(transaction['receiver']['identity']?['name'] ?? ''); transBC[i].add('- ' + amount.toString()); } // transBC[i].add(''); //transaction comment i++; } return transBC; } FetchMoreOptions? checkQueryResult(result, opts, _pubkey) { final List? blockchainTX = (result.data['transaction_connection']['edges'] as List?); // final List mempoolTX = // (result.data['txsHistoryMp']['receiving'] as List); pageInfo = result.data['transaction_connection']['pageInfo']; fetchMoreCursor = pageInfo!['endCursor']; if (fetchMoreCursor == null) nPage = 1; log.d(fetchMoreCursor); if (nPage == 1) { nRepositories = 40; } else if (nPage == 2) { nRepositories = 100; } // nRepositories = 10; nPage++; if (fetchMoreCursor != null) { opts = FetchMoreOptions( variables: {'cursor': fetchMoreCursor, 'number': nRepositories}, updateQuery: (previousResultData, fetchMoreResultData) { final List repos = [ ...previousResultData!['transaction_connection']['edges'] as List, ...fetchMoreResultData!['transaction_connection']['edges'] as List ]; log.d('repos: ' + previousResultData.toString()); log.d('repos: ' + fetchMoreResultData.toString()); log.d('repos: ' + repos.toString()); fetchMoreResultData['transaction_connection']['edges'] = repos; return fetchMoreResultData; }, ); } log.d( "###### DEBUG H Parse blockchainTX list. Cursor: $fetchMoreCursor ######"); if (fetchMoreCursor != null) { transBC = parseHistory(blockchainTX, _pubkey); } else { log.i("###### DEBUG H - Début de l'historique"); } return opts; } num removeDecimalZero(double n) { String result = n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 2); return num.parse(result); } // checkHistoryResult( // QueryResult result, FetchMoreOptions options, String address) {} }