Remove states management, keep just history pagination mechanic, clean code.

This commit is contained in:
poka 2021-01-01 21:47:43 +01:00
parent 7652b8e602
commit 9a83df3d00
5 changed files with 216 additions and 567 deletions

View File

@ -1,260 +1,11 @@
import 'dart:math';
import "package:dio/dio.dart" as dio;
import 'package:gql/language.dart' as gqlLang;
import 'package:gql_dio_link/gql_dio_link.dart';
import 'package:gql_exec/gql_exec.dart';
import "package:gql_link/gql_link.dart";
import 'package:intl/intl.dart';
// Configure node
// const graphqlEndpoint = "https://g1.librelois.fr/gva";
const graphqlEndpoint = 'http://127.0.0.1:10060/gva';
// // Check node connection
// Future getHttp() async {
// try {
// final client = await dio.Dio().get(graphqlEndpoint);
// print(client);
// return 0;
// } catch (e) {
// print(e);
// return e;
// }
// }
// Build queries
Future buildQ(query) async {
var client;
try {
client = dio.Dio();
print(client);
} catch (e) {
print(e);
}
// final client = dio.Dio();
Link link;
link = DioLink(
graphqlEndpoint,
client: client,
);
try {
final res = await link
.request(Request(
operation: Operation(document: gqlLang.parseString(query)),
))
.first;
return res;
} catch (e) {
print("Erreur: Noeud injoingnable.");
return 2;
}
}
/* Requests functions */
// Get current UD
Future getUD() async {
const query = """{
currentUd {
amount
base
}
}""";
final result = await buildQ(query);
return result.data["currentUd"]["amount"];
}
// Get balance
Future getBalance(String pubkey) async {
var query = """{
balance(script: "$pubkey") {
amount
base
}
}""";
final result = await buildQ(query);
return result.data["balance"]["amount"] / 100;
}
// // Get history
// Future getHistory(String pubkey) async {
// print(pubkey);
// var number = 20;
// var query = """{
// txsHistoryBc(
// pubkeyOrScript: "$pubkey"
// pagination: { pageSize: $number, ord: DESC }
// ) {
// both {
// pageInfo {
// hasPreviousPage
// hasNextPage
// startCursor
// endCursor
// }
// edges {
// direction
// node {
// currency
// issuers
// outputs
// comment
// writtenTime
// }
// }
// }
// }
// txsHistoryMp(pubkey: "$pubkey") {
// receiving {
// currency
// issuers
// comment
// outputs
// writtenTime
// }
// sending {
// currency
// issuers
// comment
// outputs
// writtenTime
// }
// }
// currentUd {
// amount
// base
// }
// }""";
// final res = await buildQ(query);
// // Parse history
// var resBC, resMP;
// print(res.toString());
// try {
// resBC = res.data["txsHistoryBc"]['both']['edges'];
// resMP = res.data["txsHistoryMp"];
// } catch (e) {
// return false;
// }
// var i = 0;
// // String outPubkey;
// var transBC = [];
// final currentBase = res.data['currentUd']['base'];
// final currentUD = res.data['currentUd']['amount'] / 100;
// // Get tx blockchain
// for (final trans in resBC) {
// var direction = trans['direction'];
// print(trans);
// final transaction = trans['node'];
// var output = transaction['outputs'][0];
// print("DEBUG1 " + transaction['writtenTime'].toString());
// transBC.add(i);
// transBC[i] = [];
// transBC[i].add(transaction['writtenTime']);
// var amountBrut = int.parse(output.split(':')[0]);
// final base = int.parse(output.split(':')[1]);
// final applyBase = base - currentBase;
// final amount = amountBrut * pow(10, applyBase) / 100;
// var amountUD = amount / currentUD;
// if (direction == "RECEIVED") {
// transBC[i].add(transaction['issuers'][0]);
// transBC[i].add(amount);
// transBC[i].add(amountUD.toStringAsFixed(2));
// } else if (direction == "SENT") {
// final outPubkey = output.split("SIG(")[1].replaceAll(')', '');
// transBC[i].add(outPubkey);
// transBC[i].add(-amount);
// transBC[i].add(amountUD.toStringAsFixed(2));
// }
// transBC[i].add(transaction['comment']);
// transBC[i].add(base);
// i++;
// }
// // Get tx mempool
// var transMP = [];
// i = 0;
// for (var transaction in resMP['receiving']) {
// if (transMP == null) {
// break;
// }
// var output = transaction['outputs'][0];
// var outPubkey = output.split("SIG(")[1].replaceAll(')', '');
// transMP.add(i);
// transMP[i] = [];
// transMP[i].add(transaction['writtenTime']);
// transMP[i].add(transaction['issuers'][0]);
// var amountBrut = int.parse(output.split(':')[0]);
// final base = int.parse(output.split(':')[1]);
// final applyBase = base - currentBase;
// final amount = amountBrut * pow(10, applyBase) / 100;
// transMP[i].add(amount);
// final amountUD = amount / currentUD;
// transMP[i].add(amountUD.toStringAsFixed(2));
// transMP[i].add(transaction['comment']);
// transMP[i].add(base);
// transMP[i].add(outPubkey);
// i++;
// }
// transMP = [];
// i = 0;
// for (var transaction in resMP['sending']) {
// if (transMP == null) {
// break;
// }
// var output = transaction['outputs'][0];
// var outPubkey = output.split("SIG(")[1].replaceAll(')', '');
// transMP.add(i);
// transMP[i] = [];
// transMP[i].add(transaction['writtenTime']);
// transMP[i].add(transaction['issuers'][0]);
// var amountBrut = int.parse(output.split(':')[0]);
// final base = int.parse(output.split(':')[1]);
// final applyBase = base - currentBase;
// final amount = amountBrut * pow(10, applyBase) / 100;
// transMP[i].add(-amount);
// final amountUD = amount / currentUD;
// transMP[i].add(amountUD.toStringAsFixed(2));
// transMP[i].add(transaction['comment']);
// transMP[i].add(base);
// transMP[i].add(outPubkey);
// i++;
// }
// // Order transactions by date
// transBC.sort((b, a) => Comparable.compare(a[0], b[0]));
// transMP.sort((b, a) => Comparable.compare(a[0], b[0]));
// // // Keep only base if there is base change
// // var lastBase = 0;
// // for (i in trans) {
// // if (i[5] == lastBase){
// // i[6] = null;
// // }
// // else {
// // lastBase = i[6];
// // }
// // print(trans);
// return [transBC, transMP];
// }
// NEW WAY //
const graphqlEndpoint = "https://g1.librelois.fr/gva";
// const graphqlEndpoint = 'http://192.168.1.91:10060/gva';
List parseHistory(txs) {
// print(blockchainTX[0]['node']['comment']);
var transBC = [];
int i = 0;
@ -263,12 +14,9 @@ List parseHistory(txs) {
for (final trans in txs) {
var direction = trans['direction'];
// print(trans);
final transaction = trans['node'];
var output = transaction['outputs'][0];
print("DEBUG comment: " + transaction['comment'].toString());
transBC.add(i);
transBC[i] = [];
final dateBrut =
@ -277,6 +25,8 @@ List parseHistory(txs) {
final date = formatter.format(dateBrut);
transBC[i].add(transaction['writtenTime']);
transBC[i].add(date);
print(
"DEBUG date et comment: ${date.toString()} -- ${transaction['comment'].toString()}");
var amountBrut = int.parse(output.split(':')[0]);
final base = int.parse(output.split(':')[1]);
final applyBase = base - currentBase;
@ -298,6 +48,5 @@ List parseHistory(txs) {
}
// transBC.sort((b, a) => Comparable.compare(a[0], b[0]));
return transBC;
}

View File

@ -4,263 +4,30 @@ import 'dart:typed_data';
import 'dart:ui';
import 'package:permission_handler/permission_handler.dart';
import 'package:qrscan/qrscan.dart' as scanner;
import 'api.dart';
// import "package:dio/dio.dart";
import 'package:graphql_flutter/graphql_flutter.dart';
import 'api.dart';
import 'query.dart';
//ignore: must_be_immutable
class HistoryListScreen extends StatefulWidget {
@override
_HistoryListScreenState createState() => _HistoryListScreenState();
}
class _HistoryListScreenState extends State<HistoryListScreen>
with ChangeNotifier {
class HistoryListScreen extends StatelessWidget with ChangeNotifier {
Uint8List bytes = Uint8List(0);
TextEditingController _outputPubkey;
TextEditingController _outputBalance;
final TextEditingController _outputPubkey = new TextEditingController();
final nRepositories = 3;
var pubkey = '';
String pubkey = 'D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU'; // For debug
bool isBuilding = true; // Just for debug
ScrollController _scrollController = new ScrollController();
@override
initState() {
super.initState();
this._outputPubkey = new TextEditingController();
this._outputBalance = new TextEditingController();
// checkNode().then((result) {
// setState(() {
// _result = result;
// });
// });
}
@override
Widget build(BuildContext context) {
pubkey = 'D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU';
// var pubkey = '';
print('Build state : ' + pubkey);
print('Build pubkey : ' + pubkey);
print('Build this.pubkey : ' + this.pubkey);
print('isBuilding: ' + isBuilding.toString());
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey[300],
body: Container(
body: SafeArea(
child: Column(
children: <Widget>[
SizedBox(height: 20),
TextField(
// enabled: false,
onChanged: (text) {
print("Clé tappxé: $text");
pubkey = text;
// pubkey =
isPubkey(text);
},
controller: this._outputPubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Tappez/Collez une clé publique, ou scannez',
hintStyle: TextStyle(fontSize: 15),
contentPadding:
EdgeInsets.symmetric(horizontal: 7, vertical: 15),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
),
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
TextField(
// Affichage balance
enabled: false,
controller: this._outputBalance,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: '',
hintStyle: TextStyle(fontSize: 15),
contentPadding:
EdgeInsets.symmetric(horizontal: 7, vertical: 15),
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
),
style: TextStyle(fontSize: 30.0, color: Colors.black)),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Query(
options: QueryOptions(
document: gql(getHistory),
variables: <String, dynamic>{
'pubkey': pubkey, // pubkey,
'number': nRepositories,
// set cursor to null so as to start at the beginning
// 'cursor': null
},
),
builder: (QueryResult result,
{refetch, FetchMore fetchMore}) {
if (result.isLoading && result.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (result.hasException) {
return Text(
'\nErrors: \n ' + result.exception.toString());
}
if (result.data == null &&
result.exception.toString() == null) {
return const Text('Both data and errors are null');
}
final List<dynamic> blockchainTX =
(result.data['txsHistoryBc']['both']['edges']
as List<dynamic>);
// final List<dynamic> mempoolTX =
// (result.data['txsHistoryBc']['both']['edges']
// as List<dynamic>);
final Map pageInfo =
result.data['txsHistoryBc']['both']['pageInfo'];
final String fetchMoreCursor =
pageInfo['endCursor'] ?? 'cest null...';
FetchMoreOptions opts = FetchMoreOptions(
variables: {'cursor': fetchMoreCursor},
updateQuery:
(previousResultData, fetchMoreResultData) {
// this is where you combine your previous data and response
// in this case, we want to display previous repos plus next repos
// so, we combine data in both into a single list of repos
final List<dynamic> repos = [
...previousResultData['txsHistoryBc']['both']
['edges'] as List<dynamic>,
...fetchMoreResultData['txsHistoryBc']['both']
['edges'] as List<dynamic>
];
fetchMoreResultData['txsHistoryBc']['both']
['edges'] = repos;
print('DEBUG NULL OPTION: ');
print(fetchMoreResultData);
print(fetchMoreCursor);
return fetchMoreResultData;
},
);
_scrollController
..addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (!result.isLoading) {
print('DEBUG NULL scrollController: ' +
fetchMoreCursor);
fetchMore(opts);
}
}
});
print(
'DEBUG blockchainTX: ' + blockchainTX.toString());
List transBC = parseHistory(blockchainTX);
// parseHistory(mempoolTX);
return Expanded(
child: ListView(
controller: _scrollController,
children: <Widget>[
for (var repository in transBC)
Card(
// 1
elevation: 2.0,
// 2
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(3.0)),
// 3
child: Padding(
padding: const EdgeInsets.all(100.0),
// 4
child: Column(
children: <Widget>[
SizedBox(
height: 8.0,
),
Text(
// Date
repository[1].toString(),
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w300,
),
),
Text(
// Issuer
repository[2],
style: TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w500,
),
),
Text(
// Amount
repository[3].toString(),
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w500,
),
),
// Text(
// // amountUD
// repository[4].toString(),
// style: TextStyle(
// fontSize: 12.0,
// fontWeight: FontWeight.w500,
// ),
// ),
Text(
// Comment
repository[5].toString(),
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
if (result.isLoading)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
),
],
),
);
},
),
],
)),
],
children: masterHome,
),
),
floatingActionButton: Container(
@ -279,17 +46,125 @@ class _HistoryListScreenState extends State<HistoryListScreen>
)));
}
// Future checkNode() async {
// final response = await Dio().post(graphqlEndpoint);
// showHistory(response);
// return response;
// }
List<Widget> get masterHome {
return <Widget>[
SizedBox(height: 20),
TextField(
onChanged: (text) {
print("Clé tappxé: $text");
this.pubkey = text;
isPubkey(text);
},
controller: this._outputPubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Tappez/Collez une clé publique, ou scannez',
hintStyle: TextStyle(fontSize: 15),
contentPadding: EdgeInsets.symmetric(horizontal: 7, vertical: 15),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
),
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
historyQuery(),
];
}
Expanded historyQuery() {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Query(
options: QueryOptions(
document: gql(getHistory),
variables: <String, dynamic>{
'pubkey': this.pubkey,
'number': nRepositories,
// set cursor to null so as to start at the beginning
'cursor': null
},
),
builder: (QueryResult result, {refetch, FetchMore fetchMore}) {
if (result.isLoading && result.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (result.hasException) {
return Text('\nErrors: \n ' + result.exception.toString());
}
if (result.data == null && result.exception.toString() == null) {
return const Text('Both data and errors are null');
}
final List<dynamic> blockchainTX =
(result.data['txsHistoryBc']['both']['edges'] as List<dynamic>);
final Map pageInfo =
result.data['txsHistoryBc']['both']['pageInfo'];
final String fetchMoreCursor =
pageInfo['endCursor'] ?? 'cest null...';
FetchMoreOptions opts = FetchMoreOptions(
variables: {'cursor': fetchMoreCursor},
updateQuery: (previousResultData, fetchMoreResultData) {
final List<dynamic> repos = [
...previousResultData['txsHistoryBc']['both']['edges']
as List<dynamic>,
...fetchMoreResultData['txsHistoryBc']['both']['edges']
as List<dynamic>
];
fetchMoreResultData['txsHistoryBc']['both']['edges'] = repos;
return fetchMoreResultData;
},
);
_scrollController
..addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (!result.isLoading) {
print(
"DEBUG H fetchMoreCursor in scrollController: $fetchMoreCursor");
fetchMore(opts);
}
}
});
print(
"###### DEBUG H Parse blockchainTX list. Cursor: $fetchMoreCursor ######");
List _transBC = parseHistory(blockchainTX);
return Expanded(
child: HistoryListView(
scrollController: _scrollController,
transBC: _transBC,
historyData: result),
);
},
),
],
));
}
Future _scan() async {
await Permission.camera.request();
String barcode = await scanner.scan();
// this._outputPubkey.text = "";
if (barcode != null) {
this._outputPubkey.text = barcode;
isPubkey(barcode);
}
return barcode;
@ -307,46 +182,92 @@ class _HistoryListScreenState extends State<HistoryListScreen>
pubkey.length > 42 &&
pubkey.length < 45) {
print("C'est une pubkey !!!");
showHistory(pubkey);
// var tata = _scrollController;
notifyListeners();
setState(() {
print('setPubkey: ' + pubkey);
pubkey = pubkey;
// // fetchMoreCursor = fetchMoreCursor;
});
return pubkey;
} else {
return '';
}
return '';
}
}
class HistoryListView extends StatelessWidget {
const HistoryListView(
{Key key,
@required ScrollController scrollController,
@required this.transBC,
@required this.historyData})
: _scrollController = scrollController,
super(key: key);
final ScrollController _scrollController;
final List transBC;
final historyData;
@override
Widget build(BuildContext context) {
return ListView(
controller: _scrollController,
children: <Widget>[
for (var repository in transBC)
Card(
// 1
elevation: 2.0,
// 2
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(3.0)),
// 3
child: Padding(
padding: const EdgeInsets.all(100.0),
// 4
child: Column(
children: <Widget>[
SizedBox(
height: 8.0,
),
Text(
// Date
repository[1].toString(),
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w300,
),
),
Text(
// Issuer
repository[2],
style: TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w500,
),
),
Text(
// Amount
repository[3].toString(),
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w500,
),
),
Text(
// Comment
repository[5].toString(),
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
if (historyData.isLoading)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
),
],
);
}
Future showHistory(pubkey) async {
// String pubkey = await _scan();
if (pubkey == null) {
print('nothing return.');
} else {
this._outputPubkey.text = "";
this._outputBalance.text = "";
// final udValue = await getUD();
this._outputPubkey.text = pubkey;
final myBalance = await getBalance(pubkey.toString());
this._outputBalance.text = myBalance.toString() + " Ğ1";
}
}
// Future _generateBarCode(String inputCode) async {
// if (inputCode != null && inputCode.isNotEmpty) {
// // print("Résultat du scan: " + inputCode);
// Uint8List result = await scanner.generateBarCode(inputCode);
// this.setState(() => this.bytes = result);
// } else {
// print("Veuillez renseigner une clé publique");
// }
// }
}

View File

@ -11,15 +11,13 @@ class Gecko extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _httpLink = HttpLink(
'http://127.0.0.1:30901/gva',
// defaultHeaders: <String, String>{
// 'Content-Type': 'application/json',
// },
// 'http://192.168.1.91:10060/gva',
'https://g1.librelois.fr/gva',
);
final _client = ValueNotifier(
GraphQLClient(
cache: GraphQLCache(),
cache: GraphQLCache(store: null),
link: _httpLink,
),
);

View File

@ -99,13 +99,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
dio:
dependency: "direct main"
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.10"
fake_async:
dependency: transitive
description:
@ -170,13 +163,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
gql_dio_link:
dependency: "direct main"
description:
name: gql_dio_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4"
gql_error_link:
dependency: transitive
description:
@ -185,7 +171,7 @@ packages:
source: hosted
version: "0.1.1-alpha+1601131172858"
gql_exec:
dependency: "direct main"
dependency: transitive
description:
name: gql_exec
url: "https://pub.dartlang.org"
@ -199,7 +185,7 @@ packages:
source: hosted
version: "0.3.2"
gql_link:
dependency: "direct main"
dependency: transitive
description:
name: gql_link
url: "https://pub.dartlang.org"
@ -283,7 +269,7 @@ packages:
source: hosted
version: "2.2.3"
intl:
dependency: "direct main"
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"

View File

@ -23,16 +23,11 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_launcher_icons: "^0.8.0"
qrscan: ^0.2.21
permission_handler:
image_gallery_saver:
image_picker:
dio:
gql_dio_link:
gql_exec:
gql_link:
intl:
flutter_launcher_icons: "^0.8.0"
infinite_scroll_pagination: ^2.2.3
graphql_flutter: ^4.0.0-beta.6 #^3.1.0