diff --git a/lib/data/respository.dart b/lib/data/respository.dart new file mode 100644 index 0000000..cbc5273 --- /dev/null +++ b/lib/data/respository.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:readwenderlich/data/stores/in_memory_store.dart'; +import 'package:readwenderlich/data/stores/remote/remote_store.dart'; +import 'package:readwenderlich/entities/article.dart'; +import 'package:readwenderlich/entities/article_category.dart'; +import 'package:readwenderlich/entities/article_difficulty.dart'; +import 'package:readwenderlich/entities/article_platform.dart'; +import 'package:readwenderlich/entities/list_page.dart'; +import 'package:readwenderlich/entities/sort_method.dart'; + +/// Gets data from both [RemoteStore] and [InMemoryStore]. +class Repository { + const Repository({ + @required this.remoteStore, + @required this.inMemoryStore, + }) : assert(remoteStore != null), + assert(inMemoryStore != null); + final RemoteStore remoteStore; + final InMemoryStore inMemoryStore; + + Future> getArticleListPage({ + int number, + int size, + List filteredPlatformIds, + List filteredCategoryIds, + List filteredDifficulties, + SortMethod sortMethod, + }) => + remoteStore.getArticleListPage( + number: number, + size: size, + filteredPlatformIds: filteredPlatformIds, + filteredCategoryIds: filteredCategoryIds, + filteredDifficulties: filteredDifficulties, + sortMethod: sortMethod, + ); \ No newline at end of file diff --git a/lib/home.dart b/lib/home.dart new file mode 100644 index 0000000..8f34a47 --- /dev/null +++ b/lib/home.dart @@ -0,0 +1,312 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:qrscan/qrscan.dart' as scanner; +import 'package:intl/intl.dart'; +import 'api.dart'; +import "package:dio/dio.dart"; +import 'ui/history_list_view.dart'; + +/// Integrates a list of articles with [ListPreferencesScreen]. +class HistoryListScreen extends StatefulWidget { + @override + _HistoryListScreenState createState() => _HistoryListScreenState(); +} + + +// class HistoryListScreen extends StatefulWidget { +// // GeckoHome({Key key, this.title}) : super(key: key); +// // final String title; + +// const HistoryListScreen({ +// @required this.repository, +// final String title, +// Key key, +// }) : assert(repository != null), +// super(key: key); + +// final Repository repository; + +// @override +// _GeckoHomeState createState() => _GeckoHomeState(); +// } + +class _HistoryListScreenState extends State { + Uint8List bytes = Uint8List(0); + TextEditingController _outputPubkey; + TextEditingController _outputBalance; + TextEditingController _outputHistory; + + @override + initState() { + super.initState(); + this._outputPubkey = new TextEditingController(); + this._outputBalance = new TextEditingController(); + this._outputHistory = new TextEditingController(); + // checkNode().then((result) { + // setState(() { + // _result = result; + // }); + // }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + backgroundColor: Colors.grey[300], + body: SafeArea( + child: Column( + children: [ + SizedBox(height: 20), + TextField( + // enabled: false, + onChanged: (text) { + print("Clé tappé: $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)), + 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: HistoryListView( + repository: Provider.of(context) + )) + ], + ), + ), + floatingActionButton: Container( + height: 80.0, + width: 80.0, + child: FittedBox( + child: FloatingActionButton( + onPressed: () => _scan(), + // label: Text('Scanner'), + child: Container( + height: 40.0, + width: 40.0, + child: Image.asset('images/scanner.png')), + backgroundColor: Color.fromARGB(500, 204, 255, 255), + ), + ), + ))); + } + + Widget buildTranscationCard(Transaction transaction) { + return Card( + // 1 + elevation: 2.0, + // 2 + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)), + // 3 + child: Padding( + padding: const EdgeInsets.all(16.0), + // 4 + child: Column( + children: [ + // 5 + SizedBox( + height: 14.0, + ), + // 6 + Text( + transaction.pubkey, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w700, + fontFamily: "Palatino", + ), + ), + Text(transaction.date, + style: TextStyle( + fontSize: 20.0, + fontWeight: FontWeight.w700, + fontFamily: "Palatino", + )) + ], + ), + ), + ); + } + + Future checkNode() async { + final response = await Dio().post(graphqlEndpoint); + showHistory(response); + return response; + } + + Future _scan() async { + await Permission.camera.request(); + String barcode = await scanner.scan(); + // this._outputPubkey.text = ""; + if (barcode != null) { + isPubkey(barcode); + } + return barcode; + } + + Future isPubkey(pubkey) async { + // final validCharacters = RegExp(r'^[a-zA-Z0-9]+$'); + RegExp regExp = new RegExp( + r'^[a-zA-Z0-9]+$', + caseSensitive: false, + multiLine: false, + ); + + if (regExp.hasMatch(pubkey) == true && + pubkey.length > 42 && + pubkey.length < 45) { + print("C'est une pubkey !!!"); + print(pubkey.length); + showHistory(pubkey); + } + } + + Future showHistory(pubkey) async { + // String pubkey = await _scan(); + if (pubkey == null) { + print('nothing return.'); + } else { + this._outputPubkey.text = ""; + this._outputBalance.text = ""; + this._outputHistory.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"); + // } + // } + +} + +class Transaction { + String pubkey; + String date; + + Transaction(this.pubkey, this.date); + + // TODO: Build this list !!!! + // static List samples = List getHistory(pubkey.toString()); + + Future buildHistory() async { + final myHistory = await getHistory(pubkey.toString()); + if (myHistory == false) { + return false; + } + + String historyBC = ""; + for (var i in myHistory[0]) { + var dateBrut = i[0]; + dateBrut = DateTime.fromMillisecondsSinceEpoch(dateBrut * 1000); + final DateFormat formatter = DateFormat('dd-MM-yy - H:M'); + final String date = formatter.format(dateBrut); + final issuer = i[1]; + final amount = i[2]; + // final amountUD = i[3]; + final comment = i[4]; + historyBC += date.toString() + + " \n " + + issuer.toString() + + " \n " + + amount.toString() + + " Ğ1\n " + + comment.toString() + + "\n---\n"; + } + + String historyMP = ""; + for (var i in myHistory[1]) { + if (i == null) { + break; + } + var dateBrut = "Now"; + final issuer = i[1]; + final amount = i[2]; + // final amountUD = i[3]; + final comment = i[4]; + historyMP += dateBrut.toString() + + " \n " + + issuer.toString() + + " \n " + + amount.toString() + + " Ğ1\n " + + comment.toString() + + "\n------------------\n"; + } + + var history; + // print(historyMP.toString()); + if (historyMP == "") { + history = historyBC; + } else { + history = "EN COURS DE TRAITEMENT\n" + historyMP + "VALIDÉ\n" + historyBC; + } + // this._outputHistory.text = history; + + List samples = List + + return myHistory[0]; + } + + // static List samples = buildHistory(); + + var list = json + .decode(response.body)['results'] + .map((data) => Model.fromJson(data)) + .toList(); + + // static List samples = [ + // Transaction( + // "Spaghetti and Meatballs", + // "assets/2126711929_ef763de2b3_w.jpg", + // ), + // Transaction( + // "Tomato Soup", + // "assets/27729023535_a57606c1be.jpg", + // ) + // ]; +} diff --git a/lib/main.dart b/lib/main.dart index 049b0c3..c7b2839 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,283 +1,20 @@ -import 'dart:async'; -import 'dart:typed_data'; -import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; // import 'package:image_gallery_saver/image_gallery_saver.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:qrscan/qrscan.dart' as scanner; -import 'package:intl/intl.dart'; // import 'package:flutter_html_view'; -import 'api.dart'; -import "package:dio/dio.dart"; - -List litems = ["1", "2", "Third", "4"]; +import 'home.dart'; void main() { - runApp(MyApp()); + runApp(Gecko()); } -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { - Uint8List bytes = Uint8List(0); - TextEditingController _outputPubkey; - TextEditingController _outputBalance; - TextEditingController _outputHistory; - - @override - initState() { - super.initState(); - this._outputPubkey = new TextEditingController(); - this._outputBalance = new TextEditingController(); - this._outputHistory = new TextEditingController(); - // checkNode().then((result) { - // setState(() { - // _result = result; - // }); - // }); - } - +class Gecko extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - home: Scaffold( - backgroundColor: Colors.grey[300], - body: Builder( - builder: (BuildContext context) { - return ListView( - children: [ - Container( - color: Colors.white, - child: Column( - children: [ - SizedBox(height: 20), - TextField( - // enabled: false, - onChanged: (text) { - print("Clé tappé: $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)), - 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)), - - TextField( - - // Affichage history - enabled: false, - controller: this._outputHistory, - maxLines: null, - keyboardType: TextInputType.multiline, - decoration: InputDecoration( - prefixIcon: Icon(Icons.wrap_text), - 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: 13.0, - height: 1.5, - color: Colors.black)), - SizedBox(height: 20), - SizedBox(height: 70), - // Expanded( - // child: ListView.builder( - // padding: const EdgeInsets.all(8), - // itemCount: names.length, - // itemBuilder: (BuildContext context, int index) { - // return Container( - // height: 50, - // margin: EdgeInsets.all(2), - // child: Center( - // child: Text( - // '${names[index]} (${msgCount[index]})', - // style: TextStyle(fontSize: 18), - // )), - // ); - // })) - ], - ), - ), - // new ListView.builder( - // itemCount: litems.length, - // itemBuilder: (BuildContext ctxt, int index) { - // return new Text(litems[index]); - // }) - ], - ); - }, - ), - floatingActionButton: Container( - height: 80.0, - width: 80.0, - child: FittedBox( - child: FloatingActionButton( - onPressed: () => _scan(), - // label: Text('Scanner'), - child: Container( - height: 40.0, - width: 40.0, - child: Image.asset('images/scanner.png')), - backgroundColor: Color.fromARGB(500, 204, 255, 255), - ), - ), - ))); - } - - Future checkNode() async { - final response = await Dio().post(graphqlEndpoint); - showHistory(response); - return response; - } - - Future _scan() async { - await Permission.camera.request(); - String barcode = await scanner.scan(); - // this._outputPubkey.text = ""; - if (barcode != null) { - isPubkey(barcode); - } - return barcode; - } - - Future isPubkey(pubkey) async { - // final validCharacters = RegExp(r'^[a-zA-Z0-9]+$'); - RegExp regExp = new RegExp( - r'^[a-zA-Z0-9]+$', - caseSensitive: false, - multiLine: false, + title: 'Ğecko', + theme: ThemeData(primaryColor: Colors.white, accentColor: Colors.black), + home: HistoryListScreen(), ); - - if (regExp.hasMatch(pubkey) == true && - pubkey.length > 42 && - pubkey.length < 45) { - print("C'est une pubkey !!!"); - print(pubkey.length); - showHistory(pubkey); - } } - - Future showHistory(pubkey) async { - // String pubkey = await _scan(); - if (pubkey == null) { - print('nothing return.'); - } else { - this._outputPubkey.text = ""; - this._outputBalance.text = ""; - this._outputHistory.text = ""; - // final udValue = await getUD(); - this._outputPubkey.text = pubkey; - final myBalance = await getBalance(pubkey.toString()); - this._outputBalance.text = myBalance.toString() + " Ğ1"; - - final myHistory = await getHistory(pubkey.toString()); - if (myHistory == false) { - return false; - } - - String historyBC = ""; - for (var i in myHistory[0]) { - var dateBrut = i[0]; - dateBrut = DateTime.fromMillisecondsSinceEpoch(dateBrut * 1000); - final DateFormat formatter = DateFormat('dd-MM-yy - H:M'); - final String date = formatter.format(dateBrut); - final issuer = i[1]; - final amount = i[2]; - // final amountUD = i[3]; - final comment = i[4]; - historyBC += date.toString() + - " \n " + - issuer.toString() + - " \n " + - amount.toString() + - " Ğ1\n " + - comment.toString() + - "\n---\n"; - } - - String historyMP = ""; - for (var i in myHistory[1]) { - if (i == null) { - break; - } - var dateBrut = "Now"; - final issuer = i[1]; - final amount = i[2]; - // final amountUD = i[3]; - final comment = i[4]; - historyMP += dateBrut.toString() + - " \n " + - issuer.toString() + - " \n " + - amount.toString() + - " Ğ1\n " + - comment.toString() + - "\n------------------\n"; - } - - var history; - // print(historyMP.toString()); - if (historyMP == "") { - history = historyBC; - } else { - history = - "EN COURS DE TRAITEMENT\n" + historyMP + "VALIDÉ\n" + historyBC; - } - this._outputHistory.text = history; - } - } - - // 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"); - // } - // } - } diff --git a/lib/ui/exception_indicators/empty_list_indicator.dart b/lib/ui/exception_indicators/empty_list_indicator.dart deleted file mode 100644 index ea14904..0000000 --- a/lib/ui/exception_indicators/empty_list_indicator.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:flutter/cupertino.dart'; -// import 'package:readwenderlich/ui/exception_indicators/exception_indicator.dart'; - -/// Indicates that no items were found. -class EmptyListIndicator extends StatelessWidget { - @override - Widget build(BuildContext context) => const ExceptionIndicator( - title: 'Too much filtering', - message: 'We couldn\'t find any results matching your applied filters.', - assetName: 'assets/empty-box.png', - ); -} diff --git a/lib/ui/exception_indicators/error_indicator.dart b/lib/ui/exception_indicators/error_indicator.dart deleted file mode 100644 index 83f364e..0000000 --- a/lib/ui/exception_indicators/error_indicator.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -// import 'package:readwenderlich/ui/exception_indicators/generic_error_indicator.dart'; -// import 'package:readwenderlich/ui/exception_indicators/no_connection_indicator.dart'; - -/// Based on the received error, displays either a [NoConnectionIndicator] or -/// a [GenericErrorIndicator]. -class ErrorIndicator extends StatelessWidget { - const ErrorIndicator({ - @required this.error, - this.onTryAgain, - Key key, - }) : assert(error != null), - super(key: key); - - final dynamic error; - final VoidCallback onTryAgain; - - @override - Widget build(BuildContext context) => error is SocketException - ? NoConnectionIndicator( - onTryAgain: onTryAgain, - ) - : GenericErrorIndicator( - onTryAgain: onTryAgain, - ); -} diff --git a/lib/ui/exception_indicators/exception_indicator.dart b/lib/ui/exception_indicators/exception_indicator.dart deleted file mode 100644 index 89555fb..0000000 --- a/lib/ui/exception_indicators/exception_indicator.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -/// Basic layout for indicating that an exception occurred. -class ExceptionIndicator extends StatelessWidget { - const ExceptionIndicator({ - @required this.title, - @required this.assetName, - this.message, - this.onTryAgain, - Key key, - }) : assert(title != null), - assert(assetName != null), - super(key: key); - final String title; - final String message; - final String assetName; - final VoidCallback onTryAgain; - - @override - Widget build(BuildContext context) => Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16), - child: Column( - children: [ - Image.asset( - assetName, - ), - const SizedBox( - height: 32, - ), - Text( - title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headline6, - ), - if (message != null) - const SizedBox( - height: 16, - ), - if (message != null) - Text( - message, - textAlign: TextAlign.center, - ), - if (onTryAgain != null) const Spacer(), - if (onTryAgain != null) - SizedBox( - height: 50, - width: double.infinity, - child: RaisedButton.icon( - onPressed: onTryAgain, - icon: const Icon( - Icons.refresh, - color: Colors.white, - ), - label: const Text( - 'Try Again', - style: TextStyle( - fontSize: 16, - color: Colors.white, - ), - ), - ), - ), - ], - ), - ), - ); -} diff --git a/lib/ui/exception_indicators/generic_error_indicator.dart b/lib/ui/exception_indicators/generic_error_indicator.dart deleted file mode 100644 index 85dc846..0000000 --- a/lib/ui/exception_indicators/generic_error_indicator.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/cupertino.dart'; -// import 'package:readwenderlich/ui/exception_indicators/exception_indicator.dart'; - -/// Indicates that an unknown error occurred. -class GenericErrorIndicator extends StatelessWidget { - const GenericErrorIndicator({ - Key key, - this.onTryAgain, - }) : super(key: key); - - final VoidCallback onTryAgain; - - @override - Widget build(BuildContext context) => ExceptionIndicator( - title: 'Something went wrong', - message: 'The application has encountered an unknown error.\n' - 'Please try again later.', - assetName: 'assets/confused-face.png', - onTryAgain: onTryAgain, - ); -} diff --git a/lib/ui/exception_indicators/no_connection_indicator.dart b/lib/ui/exception_indicators/no_connection_indicator.dart deleted file mode 100644 index 8a62dcd..0000000 --- a/lib/ui/exception_indicators/no_connection_indicator.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/cupertino.dart'; -// import 'package:readwenderlich/ui/exception_indicators/exception_indicator.dart'; - -/// Indicates that a connection error occurred. -class NoConnectionIndicator extends StatelessWidget { - const NoConnectionIndicator({ - Key key, - this.onTryAgain, - }) : super(key: key); - - final VoidCallback onTryAgain; - - @override - Widget build(BuildContext context) => ExceptionIndicator( - title: 'No connection', - message: 'Please check internet connection and try again.', - assetName: 'assets/frustrated-face.png', - onTryAgain: onTryAgain, - ); -} diff --git a/test/widget_test.dart b/test/widget_test.dart index c59e028..e278b69 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:gecko/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(Gecko()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);