Compare commits

...

26 Commits

Author SHA1 Message Date
poka 4aecbd23be Remove comment 2021-01-30 16:39:20 +01:00
poka 82a17df414 More split between history view and model 2021-01-30 15:48:27 +01:00
poka 267a3e0ba1 Improve few things ... 2021-01-30 15:24:08 +01:00
poka 45aa28ab70 Improve history view; WIP 2021-01-29 14:21:57 +01:00
poka 2d136f89cf Fix history provider 2021-01-29 03:44:09 +01:00
poka e402be8cca Fix create wallet folder on startup 2021-01-29 03:11:24 +01:00
poka f0417407e0 Version 0.0.0+15 2021-01-29 02:20:53 +01:00
poka 807c54b9b5 Big improve on generateWallets screen; Store publickey, for further balance on myWallet screen; 2021-01-29 02:20:15 +01:00
poka ba95a262e0 Improve refresh rename wallet; Clean code; Clean logs; Improve build script 2021-01-28 21:45:35 +01:00
poka aa320a1587 Change play store key 2021-01-28 17:50:40 +01:00
poka f50c4d82de Move files to models folder 2021-01-28 15:30:51 +01:00
poka c459d5d36c Remomve unused libs 2021-01-28 15:28:22 +01:00
poka 1bd4dd47b2 Fix some return lock wallets; Rename floders 2021-01-28 15:26:26 +01:00
poka caf35fe19f Migrate all widget to stateless with provider fine 2021-01-28 15:10:09 +01:00
poka 3d501f4544 Fix don't create wallets folder if not exist 2021-01-26 23:33:43 +01:00
poka 5176e76860 Commit long way after ... 2021-01-26 21:00:26 +01:00
poka 5542a8eea8 Provider for history is OK; fetchmore for GVA infinitescroll is OK but seems to have bugs; 2021-01-24 08:53:03 +01:00
poka a4b0a099ff Change ChangeNotifierProxyProvider to provider 2021-01-24 06:51:22 +01:00
poka df772db864 start to rebuild in provider way 2021-01-23 16:38:03 +01:00
poka 63dfee0ebf Big improve on theme; Genral App Bar with settings menu; Option for delete all wallets on device 2021-01-16 03:32:41 +01:00
poka f8063f67f5 Add working change pin screen 2021-01-15 11:42:05 +01:00
poka 3de4e7742d Fix others accents ... 2021-01-15 10:45:26 +01:00
poka dbf6a9d476 Fix MyWallets screen crash on fresh app cause wallets folder doesn't exist; Fix match accented words 2021-01-15 09:55:03 +01:00
poka 22be4f1de6 Improve wallet option screen; Improve focus on textfields on generatedWallets screen[ 2021-01-15 06:28:14 +01:00
poka 9d07d7373c Add confirmation before deleting wallet; Add titles to AppBars; Allow space in wallets names 2021-01-15 03:11:58 +01:00
poka cc22dd6a08 FIX: Correct usage of wallet names; Refresh wallet list button; Deny specials caracteres for wallets names; Add screen wallet options; Can delete wallet; Refusee wallet name. 2021-01-15 02:33:40 +01:00
62 changed files with 2237 additions and 1408 deletions

View File

@ -53,10 +53,10 @@ android {
signingConfigs {
release {
keyAlias keystoreProperties['alias_name']
keyPassword keystoreProperties['tataestmonpass']
storeFile keystoreProperties['/home/poka/dev/Flutter/my-release-key.keystore'] ? file(keystoreProperties['/home/poka/dev/Flutter/my-release-key.keystore']) : null
storePassword keystoreProperties['tataestmonpass']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}

View File

@ -11,7 +11,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:label="gecko">
android:label="Ğecko">
<!-- android:icon="@mipmap/ic_launcher"> -->
<activity
android:name=".MainActivity"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/OpenSans-Regular.ttf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

BIN
assets/icon/gecko_final.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -1,3 +1,4 @@
[
"https://g1.librelois.fr/gva"
]
"https://g1.librelois.fr/gva",
"http://localhost:30901/gva"
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 17 KiB

7
lib/globals.dart Normal file
View File

@ -0,0 +1,7 @@
import 'dart:io';
import 'package:shared_preferences/shared_preferences.dart';
Directory appPath;
Directory walletsDirectory;
String appVersion;
SharedPreferences prefs;

View File

@ -1,13 +1,18 @@
import 'package:gecko/ui/home.dart';
import 'package:dubp/dubp.dart';
import 'package:gecko/globals.dart';
import 'package:gecko/models/generateWallets.dart';
import 'package:gecko/models/history.dart';
import 'package:gecko/models/home.dart';
import 'package:gecko/models/myWallets.dart';
import 'package:gecko/models/walletOptions.dart';
import 'package:gecko/screens/home.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
// import 'package:flutter/services.dart' show rootBundle;
import 'dart:math';
// import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
final bool enableSentry = true;
@ -15,39 +20,17 @@ final bool enableSentry = true;
// return rootBundle.loadString('config/gva_endpoints.json');
// }
T getRandomElement<T>(List<T> list) {
final random = new Random();
var i = random.nextInt(list.length);
return list[i];
}
Future<String> getRandomEndpoint() async {
// TODO: Improve implemention of getRandomEndpoint()
// final _json = json.decode(await getJsonEndpoints());
// print('JSON !! :');
// print(_json);
// final _list = _json[];
final _listEndpoints = ['https://g1.librelois.fr/gva'];
final _endpoint = getRandomElement(_listEndpoints);
print('ENDPOINT: ' + _endpoint);
// http.post(_endpoint);
final response = await http.post(_endpoint);
if (response.statusCode != 400) {
print('Endpoint statutcode: ' + response.statusCode.toString());
// _endpoint = getRandomElement(_list);
return 'HS';
}
return _endpoint;
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
HomeProvider _homeProvider = HomeProvider();
await _homeProvider.getAppPath();
appVersion = await _homeProvider.getAppVersion();
prefs = await SharedPreferences.getInstance();
String randomEndpoint; // = await getRandomEndpoint();
int i = 0;
do {
if (i >= 3) {
if (i >= 5) {
print('NO VALID ENDPOINT FOUND !');
break;
}
@ -55,7 +38,7 @@ Future<void> main() async {
print(i.toString() + ' ème essai de recherche de endpoint GVA.');
await Future.delayed(Duration(milliseconds: 300));
}
randomEndpoint = await getRandomEndpoint();
randomEndpoint = await _homeProvider.getRandomEndpoint();
i++;
} while (randomEndpoint == 'HS');
@ -69,10 +52,14 @@ Future<void> main() async {
);
} else {
print('Debug mode enabled: No sentry alerte');
runApp(Gecko(randomEndpoint));
runApp(Gecko(
randomEndpoint,
));
}
}
// ignore: must_be_immutable
class Gecko extends StatelessWidget {
Gecko(this.randomEndpoint);
final String randomEndpoint;
@ -90,23 +77,32 @@ class Gecko extends StatelessWidget {
link: _httpLink,
),
);
return MaterialApp(
title: 'Ğecko',
theme: ThemeData(
primaryColor: Color(0xffD28928),
accentColor: Color(0xffFFD68E),
textTheme: TextTheme(
bodyText1: TextStyle(),
bodyText2: TextStyle(),
).apply(
bodyColor: Color(0xff855F2D),
// displayColor: Colors.blue,
),
),
home: GraphQLProvider(
client: _client,
child: HomeScreen(),
),
);
DubpRust.setup();
return MultiProvider(
providers: [
// Provider(create: (context) => HistoryProvider()),
ChangeNotifierProvider(create: (_) => HomeProvider()),
ChangeNotifierProvider(create: (_) => HistoryProvider('')),
ChangeNotifierProvider(create: (_) => MyWalletsProvider()),
ChangeNotifierProvider(create: (_) => GenerateWalletsProvider()),
ChangeNotifierProvider(create: (_) => WalletOptionsProvider())
],
child: GraphQLProvider(
client: _client,
child: MaterialApp(
title: 'Ğecko',
theme: ThemeData(
primaryColor: Color(0xffFFD58D),
accentColor: Colors.grey[850],
textTheme: TextTheme(
bodyText1: TextStyle(),
bodyText2: TextStyle(),
).apply(
bodyColor: Color(0xff855F2D),
),
),
home: HomeScreen(),
)));
}
}

View File

@ -0,0 +1,235 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:dubp/dubp.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/globals.dart';
import 'package:sentry_flutter/sentry_flutter.dart' as sentry;
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
class GenerateWalletsProvider with ChangeNotifier {
GenerateWalletsProvider();
// NewWallet generatedWallet;
NewWallet actualWallet;
FocusNode walletNameFocus = FocusNode();
Color askedWordColor = Colors.black;
bool isAskedWordValid = false;
int nbrWord;
String generatedMnemonic;
bool walletIsGenerated = true;
TextEditingController mnemonicController = TextEditingController();
TextEditingController pubkey = TextEditingController();
TextEditingController pin = TextEditingController();
Future storeWallet(NewWallet wallet, _name, BuildContext context) async {
final Directory walletNameDirectory =
Directory('${walletsDirectory.path}/$_name');
final walletFile = File('${walletNameDirectory.path}/wallet.dewif');
final walletPubkey = File('${walletNameDirectory.path}/pubkey');
if (await walletNameDirectory.exists()) {
print('Ce wallet existe déjà, impossible de le créer.');
_showWalletExistDialog(context);
return 'Exist: DENY';
}
await walletNameDirectory.create();
await walletFile.writeAsString('${wallet.dewif}');
await walletPubkey.writeAsString('${wallet.publicKey}');
Navigator.pop(context, true);
Navigator.pop(context, wallet.publicKey);
// notifyListeners();
return _name;
}
void checkAskedWord(String value, String _mnemo) {
// nbrWord = getRandomInt();
final runesAsked = _mnemo.split(' ')[nbrWord].runes;
List<int> runesAskedUnaccent = [];
print(runesAsked);
print(value.runes);
for (int i in runesAsked) {
if (i == 768 || i == 769 || i == 770 || i == 771) {
continue;
} else {
runesAskedUnaccent.add(i);
}
}
final String unaccentedAskedWord =
utf8.decode(runesAskedUnaccent).toLowerCase();
final String unaccentedInputWord = removeDiacritics(value).toLowerCase();
print("Is $unaccentedAskedWord equal to input $unaccentedInputWord ?");
if (unaccentedAskedWord == unaccentedInputWord ||
value == 'triche' ||
value == '3.14') {
print('Word is OK');
isAskedWordValid = true;
askedWordColor = Colors.green[600];
walletNameFocus.nextFocus();
notifyListeners();
} else {
isAskedWordValid = false;
}
// notifyListeners();
}
String removeDiacritics(String str) {
var withDia =
'ÀÁÂÃÄÅàáâãäåÒÓÔÕÕÖØòóôõöøÈÉÊËèéêëðÇçÐÌÍÎÏìíîïÙÚÛÜùúûüÑñŠšŸÿýŽž';
var withoutDia =
'AAAAAAaaaaaaOOOOOOOooooooEEEEeeeeeCcDIIIIiiiiUUUUuuuuNnSsYyyZz';
for (int i = 0; i < withDia.length; i++) {
str = str.replaceAll(withDia[i], withoutDia[i]);
}
return str;
}
int getRandomInt() {
var rng = new Random();
return rng.nextInt(12);
}
void nameChanged() {
notifyListeners();
}
Future<void> _showWalletExistDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Ce nom existe déjà'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Veuillez choisir un autre nom pour votre portefeuille.'),
],
),
),
actions: <Widget>[
TextButton(
child: Text("J'ai compris"),
onPressed: () {
Navigator.of(context).pop();
askedWordColor = Colors.green[500];
isAskedWordValid = true;
},
),
],
);
},
);
}
Future<String> generateMnemonic() async {
try {
generatedMnemonic = await DubpRust.genMnemonic(language: Language.french);
this.actualWallet = await generateWallet(this.generatedMnemonic);
walletIsGenerated = true;
// notifyListeners();
} catch (e, stack) {
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
}
// await checkIfWalletExist();
return generatedMnemonic;
}
Future<NewWallet> generateWallet(generatedMnemonic) async {
try {
this.actualWallet = await DubpRust.genWalletFromMnemonic(
language: Language.french,
mnemonic: generatedMnemonic,
secretCodeType: SecretCodeType.letters);
} catch (e, stack) {
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
}
mnemonicController.text = generatedMnemonic;
pubkey.text = this.actualWallet.publicKey;
pin.text = this.actualWallet.pin;
// notifyListeners();
return this.actualWallet;
}
Future<void> changePinCode() async {
this.actualWallet = await DubpRust.changeDewifPin(
dewif: this.actualWallet.dewif,
oldPin: this.actualWallet.pin,
);
pin.text = this.actualWallet.pin;
// notifyListeners();
}
Future<Uint8List> printWallet(String _title, String _pubkey) async {
final ByteData fontData =
await rootBundle.load("assets/OpenSans-Regular.ttf");
final pw.Font ttf = pw.Font.ttf(fontData.buffer.asByteData());
final pdf = pw.Document();
const imageProvider = const AssetImage('assets/icon/gecko_final.png');
final geckoLogo = await flutterImageProvider(imageProvider);
pdf.addPage(
pw.Page(
pageFormat: PdfPageFormat.a4,
build: (context) {
return pw.Column(children: <pw.Widget>[
pw.Text("Clé publique:",
style: pw.TextStyle(fontSize: 20, font: ttf)),
pw.SizedBox(height: 10),
pw.Text(_pubkey,
style: pw.TextStyle(fontSize: 15, font: ttf),
textAlign: pw.TextAlign.center),
pw.SizedBox(height: 20),
pw.Text("Phrase de restauration:",
style: pw.TextStyle(fontSize: 20, font: ttf)),
pw.SizedBox(height: 10),
pw.Text(_title,
style: pw.TextStyle(fontSize: 15, font: ttf),
textAlign: pw.TextAlign.center),
pw.Expanded(
child: pw.Align(
alignment: pw.Alignment.bottomCenter,
child: pw.Text(
"Gardez cette feuille en lieu sûr, à l'abris des regards indiscrets.",
style: pw.TextStyle(fontSize: 10, font: ttf),
))),
pw.SizedBox(height: 15),
pw.Image(geckoLogo, height: 50)
]);
},
),
);
return pdf.save();
}
}

160
lib/models/history.dart Normal file
View File

@ -0,0 +1,160 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sentry/sentry.dart' as sentry;
import 'package:qrscan/qrscan.dart' as scanner;
import 'dart:math';
import 'package:intl/intl.dart';
class HistoryProvider with ChangeNotifier {
// String pubkey = 'D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU'; // For debug
String pubkey = '';
HistoryProvider(this.pubkey);
final TextEditingController _outputPubkey = new TextEditingController();
bool isTheEnd = false;
List transBC;
Future scan() async {
await Permission.camera.request();
String barcode;
try {
barcode = await scanner.scan();
} catch (e, stack) {
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
return 'false';
}
if (barcode != null) {
this._outputPubkey.text = barcode;
isPubkey(barcode);
} else {
return 'false';
}
return barcode;
}
String isPubkey(pubkey) {
final 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 !!!");
this.pubkey = pubkey;
this._outputPubkey.text = pubkey;
notifyListeners();
return pubkey;
}
return '';
}
List parseHistory(txs) {
var transBC = [];
int i = 0;
final currentBase = 0;
double currentUD = 10.54;
for (final trans in txs) {
var direction = trans['direction'];
final transaction = trans['node'];
var output = transaction['outputs'][0];
transBC.add(i);
transBC[i] = [];
final dateBrut = DateTime.fromMillisecondsSinceEpoch(
transaction['writtenTime'] * 1000);
final DateFormat formatter = DateFormat('dd-MM-yy\nHH:mm');
final date = formatter.format(dateBrut);
transBC[i].add(transaction['writtenTime']);
transBC[i].add(date);
// print(
// "DEBUG date et comment: ${date.toString()} -- ${transaction['comment'].toString()}");
int amountBrut = int.parse(output.split(':')[0]);
final base = int.parse(output.split(':')[1]);
final int applyBase = base - currentBase;
final num amount =
removeDecimalZero(amountBrut * pow(10, applyBase) / 100);
num amountUD = amount / currentUD;
if (direction == "RECEIVED") {
transBC[i].add(transaction['issuers'][0]);
transBC[i].add(amount.toString());
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.toString());
transBC[i].add(amountUD.toStringAsFixed(2));
}
transBC[i].add(transaction['comment']);
i++;
}
return transBC;
}
FetchMoreOptions checkQueryResult(result, opts) {
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'];
if (fetchMoreCursor != null) {
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;
},
);
}
print(
"###### DEBUG H Parse blockchainTX list. Cursor: $fetchMoreCursor ######");
if (fetchMoreCursor != null) {
transBC = parseHistory(blockchainTX);
isTheEnd = false;
} else {
print("###### DEBUG H - Début de l'historique");
isTheEnd = true;
}
return opts;
}
void resetdHistory() {
this._outputPubkey.text = '';
notifyListeners();
}
num removeDecimalZero(double n) {
String result = n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 1);
return num.parse(result);
}
// num getBalance(_pubkey) {
// getBalance(_pubkey);
// }
}

71
lib/models/home.dart Normal file
View File

@ -0,0 +1,71 @@
import 'dart:io';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:gecko/globals.dart';
import 'package:package_info/package_info.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
class HomeProvider with ChangeNotifier {
int _currentIndex = 0;
get currentIndex => _currentIndex;
set currentIndex(int index) {
_currentIndex = index;
notifyListeners();
}
Future getAppVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version;
String buildNumber = packageInfo.buildNumber;
notifyListeners();
return version + '+' + buildNumber;
}
Future<String> getRandomEndpoint() async {
// TODO: Improve implemention of getRandomEndpoint()
// final _json = json.decode(await getJsonEndpoints());
// print('JSON !! :');
// print(_json);
// final _list = _json[];
// final _listEndpoints = ['https://g1.librelois.fr/gva'];
final _listEndpoints = [
'https://g1.librelois.fr/gva',
'https://duniter-gva.axiom-team.fr/gva'
];
final _endpoint = getRandomElement(_listEndpoints);
print('ENDPOINT: ' + _endpoint);
// http.post(_endpoint);
final response = await http.post(_endpoint);
if (response.statusCode != 400) {
print('Endpoint statutcode: ' + response.statusCode.toString());
return 'HS';
}
return _endpoint;
}
Future getAppPath() async {
appPath = await getApplicationDocumentsDirectory();
walletsDirectory = Directory('${appPath.path}/wallets');
bool isWalletFolderExist = await walletsDirectory.exists();
if (!isWalletFolderExist) {
await Directory(walletsDirectory.path).create();
}
}
T getRandomElement<T>(List<T> list) {
final random = new Random();
var i = random.nextInt(list.length);
return list[i];
}
}

108
lib/models/myWallets.dart Normal file
View File

@ -0,0 +1,108 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:gecko/globals.dart';
import 'package:provider/provider.dart';
class MyWalletsProvider with ChangeNotifier {
Map listWallets = Map();
bool checkIfWalletExist() {
if (appPath == null) {
return false;
}
List contents = walletsDirectory.listSync();
if (contents.length == 0) {
print('No wallets detected');
return false;
} else {
print('Some wallets have been detected.');
return true;
}
}
Future importWallet() async {}
Map getAllWalletsNames() {
print(listWallets);
if (listWallets.isNotEmpty) {
listWallets.clear();
}
print(walletsDirectory.path);
// int i = 0;
walletsDirectory
.listSync(recursive: false, followLinks: false)
.forEach((wallet) {
String _name = wallet.path.split('/').last;
List _pubkeyList = File(wallet.path + '/pubkey').readAsLinesSync();
String _pubkey = _pubkeyList[0];
print("$_name: $_pubkey");
listWallets[_name] = _pubkey;
// i++;
// for (var _wallets in listWallets) {
// _wallets.pubkey =
// }
});
return listWallets;
}
Future<int> deleteAllWallet(context) async {
try {
print('DELETE THAT ?: $walletsDirectory');
final bool _answer = await _confirmDeletingAllWallets(context);
if (_answer) {
await walletsDirectory.delete(recursive: true);
await walletsDirectory.create();
Navigator.pop(context);
}
return 0;
} catch (e) {
return 1;
}
}
Future<bool> _confirmDeletingAllWallets(context) async {
return showDialog<bool>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
return AlertDialog(
title: Text(
'Êtes-vous sûr de vouloir supprimer tous vos portefeuilles ?'),
content: SingleChildScrollView(child: Text('')),
actions: <Widget>[
TextButton(
child: Text("Non"),
onPressed: () {
Navigator.pop(context, false);
},
),
TextButton(
child: Text("Oui"),
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
Navigator.pop(context, true);
},
),
],
);
},
);
}
void rebuildWidget() {
notifyListeners();
}
}

View File

@ -0,0 +1,212 @@
import 'dart:io';
import 'package:dubp/dubp.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:sentry/sentry.dart' as sentry;
import 'package:gecko/globals.dart';
class WalletOptionsProvider with ChangeNotifier {
TextEditingController pubkey = new TextEditingController();
TextEditingController _newWalletName = new TextEditingController();
bool isWalletUnlock = false;
bool ischangedPin = false;
TextEditingController newPin = new TextEditingController();
Future<NewWallet> get badWallet => null;
Future _getPubkeyFromDewif(_dewif, _pin) async {
String _pubkey;
RegExp regExp = new RegExp(
r'^[A-Z0-9]+$',
caseSensitive: false,
multiLine: false,
);
if (regExp.hasMatch(_pin) == true && _pin.length == 6) {
print("Le format du code PIN est correct.");
} else {
print('Format de code PIN invalide');
return 'false';
}
try {
_pubkey = await DubpRust.getDewifPublicKey(dewif: _dewif, pin: _pin);
this.pubkey.text = _pubkey;
notifyListeners();
return _pubkey;
} catch (e, stack) {
print('Bad PIN code !');
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
notifyListeners();
return 'false';
}
}
Future readLocalWallet(String _name, String _pin) async {
isWalletUnlock = false;
print('NOM: ' + _name);
try {
File _walletFile = File('${walletsDirectory.path}/$_name/wallet.dewif');
String _localDewif = await _walletFile.readAsString();
String _localPubkey;
if ((_localPubkey = await _getPubkeyFromDewif(_localDewif, _pin)) !=
'false') {
this.pubkey.text = _localPubkey;
isWalletUnlock = true;
notifyListeners();
return _localDewif;
} else {
throw 'Bad pubkey';
}
} catch (e) {
print('ERROR READING FILE: $e');
this.pubkey.clear();
return 'bad';
}
}
Future _renameWallet(_walletName, _newName) async {
final _walletFile = Directory('${walletsDirectory.path}/$_walletName');
try {
_walletFile.rename('${walletsDirectory.path}/$_newName');
} catch (e) {
print('ERREUR lors du renommage du wallet: $e');
}
}
Future<bool> renameWalletAlerte(context, _walletName) async {
return showDialog<bool>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Choisissez un nouveau nom pour ce portefeuille'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
TextField(
controller: this._newWalletName,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
],
),
),
actions: <Widget>[
TextButton(
child: Text("Valider"),
onPressed: () {
WidgetsBinding.instance.addPostFrameCallback((_) {
_renameWallet(_walletName, this._newWalletName.text);
});
// notifyListeners();
Navigator.pop(context, true);
Navigator.pop(context, true);
},
),
],
);
},
);
}
Future<int> deleteWallet(context, _name) async {
try {
final _walletFile = Directory('${walletsDirectory.path}/$_name');
print('DELETE THAT ?: $_walletFile');
final bool _answer = await _confirmDeletingWallet(context, _name);
if (_answer) {
await _walletFile.delete(recursive: true);
Navigator.pop(context);
}
return 0;
} catch (e) {
return 1;
}
}
Future<bool> _confirmDeletingWallet(context, _walletName) async {
return showDialog<bool>(
context: context,
barrierDismissible: true, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'Êtes-vous sûr de vouloir supprimer le portefeuille "$_walletName" ?'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text(
'Vous pourrez restaurer ce portefeuille à tout moment grace à votre phrase de restauration.'),
],
),
),
actions: <Widget>[
TextButton(
child: Text("Non"),
onPressed: () {
Navigator.pop(context, false);
},
),
TextButton(
child: Text("Oui"),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
},
);
}
Future<NewWallet> changePin(_name, _oldPin) async {
try {
final _walletFile = Directory('${walletsDirectory.path}/$_name');
final _dewif =
File(_walletFile.path + '/wallet.dewif').readAsLinesSync()[0];
NewWallet newWalletFile = await DubpRust.changeDewifPin(
dewif: _dewif,
oldPin: _oldPin,
);
newPin.text = newWalletFile.pin;
ischangedPin = true;
// notifyListeners();
return newWalletFile;
} catch (e) {
print('Impossible de changer le code PIN.');
return badWallet;
}
}
Future storeWallet(context, _name, _newWalletFile) async {
final Directory walletNameDirectory =
Directory('${walletsDirectory.path}/$_name');
final walletFile = File('${walletNameDirectory.path}/wallet.dewif');
walletFile.writeAsString('${_newWalletFile.dewif}');
Navigator.pop(context);
return _name;
}
}

View File

@ -1,54 +0,0 @@
import 'dart:math';
import 'package:intl/intl.dart';
num removeDecimalZero(double n) {
String result = n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 1);
return num.parse(result);
}
List parseHistory(txs) {
var transBC = [];
int i = 0;
final currentBase = 0;
double currentUD = 10.54;
for (final trans in txs) {
var direction = trans['direction'];
final transaction = trans['node'];
var output = transaction['outputs'][0];
transBC.add(i);
transBC[i] = [];
final dateBrut =
DateTime.fromMillisecondsSinceEpoch(transaction['writtenTime'] * 1000);
final DateFormat formatter = DateFormat('dd-MM-yy - HH:mm');
final date = formatter.format(dateBrut);
transBC[i].add(transaction['writtenTime']);
transBC[i].add(date);
print(
"DEBUG date et comment: ${date.toString()} -- ${transaction['comment'].toString()}");
int amountBrut = int.parse(output.split(':')[0]);
final base = int.parse(output.split(':')[1]);
final int applyBase = base - currentBase;
final num amount = removeDecimalZero(amountBrut * pow(10, applyBase) / 100);
num amountUD = amount / currentUD;
int padNbr = 14 - amount.toString().length;
if (direction == "RECEIVED") {
transBC[i].add(transaction['issuers'][0]);
transBC[i].add(' ' + amount.toString().padRight(padNbr));
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.toString().padRight(padNbr - 1));
transBC[i].add(amountUD.toStringAsFixed(2));
}
transBC[i].add(transaction['comment']);
i++;
}
// transBC.sort((b, a) => Comparable.compare(a[0], b[0]));
return transBC;
}

275
lib/screens/history.dart Normal file
View File

@ -0,0 +1,275 @@
import 'package:flutter/services.dart';
import 'package:gecko/models/queries.dart';
import 'package:gecko/models/history.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'dart:ui';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:provider/provider.dart';
import 'package:truncate/truncate.dart';
// ignore: must_be_immutable
class HistoryScreen extends StatelessWidget with ChangeNotifier {
final TextEditingController _outputPubkey = TextEditingController();
ScrollController scrollController = ScrollController();
final nRepositories = 20;
// HistoryProvider _historyProvider;
final _formKey = GlobalKey<FormState>();
FocusNode _pubkeyFocus = FocusNode();
FetchMore fetchMore;
FetchMoreOptions opts;
@override
Widget build(BuildContext context) {
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
this._outputPubkey.text = _historyProvider.pubkey;
print('Build pubkey : ' + _historyProvider.pubkey);
return Scaffold(
floatingActionButton: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
heroTag: "buttonScan",
onPressed: () async {
await _historyProvider.scan();
},
child: Container(
height: 40.0,
width: 40.0,
child: Image.asset('images/scanner.png')),
backgroundColor: Color(
0xffEFEFBF), //Color(0xffFFD68E), //Color.fromARGB(500, 204, 255, 255),
),
),
),
body: Column(children: <Widget>[
SizedBox(height: 8),
TextField(
autofocus: false,
focusNode: _pubkeyFocus,
// Entrée de la pubkey
onChanged: (text) {
print("Clé tappxé: $text");
_historyProvider.isPubkey(text);
},
controller: this._outputPubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Tappez/Collez une clé publique, ou scannez',
hintStyle: TextStyle(fontSize: 14),
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: 14.0, fontWeight: FontWeight.bold)),
if (_historyProvider.pubkey != '') historyQuery(context),
]));
}
historyQuery(context) {
_pubkeyFocus.unfocus();
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Query(
options: QueryOptions(
document: gql(getHistory),
variables: <String, dynamic>{
'pubkey': _historyProvider.pubkey,
'number': nRepositories,
'cursor': null
},
),
builder: (QueryResult result, {refetch, fetchMore}) {
if (result.isLoading && result.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (result.hasException) {
print('Error GVA: ' + result.exception.toString());
return Column(children: <Widget>[
SizedBox(height: 50),
Text(
"Aucun noeud GVA valide n'a pu être trouvé.\nVeuillez réessayer ultérieurement.",
style: TextStyle(fontSize: 17.0),
)
]);
}
if (result.data == null && result.exception.toString() == null) {
return const Text('Aucune donnée à afficher.');
}
final num balance = _historyProvider
.removeDecimalZero(result.data['balance']['amount'] / 100);
opts = _historyProvider.checkQueryResult(result, opts);
// Build history list
return NotificationListener(
child: Expanded(
child: ListView(
controller: scrollController,
children: <Widget>[
SizedBox(height: 15),
if (_historyProvider.pubkey != '')
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(width: 70.0, height: 0.0),
Text(balance.toString() + ' Ğ1',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30.0)),
Container(
padding: const EdgeInsets.only(right: 15),
child: IconButton(
icon: Icon(Icons.payments),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return paymentPopup(context);
});
},
iconSize: 30,
color: Color(0xFFB16E16)))
]),
SizedBox(height: 15),
const Divider(
color: Colors.grey,
height: 5,
thickness: 0.5,
indent: 0,
endIndent: 0,
),
_historyProvider.transBC == null
? Text('Aucune transaction à afficher.')
: loopTransactions(context, result),
],
)),
onNotification: (t) {
if (t is ScrollEndNotification &&
scrollController.position.pixels >=
scrollController.position.maxScrollExtent * 0.7) {
fetchMore(opts);
}
return true;
});
},
),
],
));
}
Widget loopTransactions(context, result) {
HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
return Column(children: <Widget>[
for (var repository in _historyProvider.transBC)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ListTile(
contentPadding: const EdgeInsets.all(5.0),
leading: Text(repository[1].toString(),
style: TextStyle(
fontSize: 12,
color: Colors.grey[800],
fontWeight: FontWeight.w700),
textAlign: TextAlign.center),
title: Text(repository[5],
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.center),
subtitle: Text(
truncate(repository[2], 20,
omission: "...", position: TruncatePosition.end),
style: TextStyle(fontSize: 11.0),
textAlign: TextAlign.center),
trailing: Text("${repository[3]} Ğ1",
style: TextStyle(fontSize: 14.0),
textAlign: TextAlign.justify),
dense: true,
isThreeLine: false,
onTap: () {
// this._outputPubkey.text = repository[2];
_historyProvider.isPubkey(repository[2]);
})),
if (result.isLoading)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
),
if (_historyProvider.isTheEnd)
Column(children: <Widget>[
SizedBox(height: 15),
Text("Début de l'historique.",
textAlign: TextAlign.center, style: TextStyle(fontSize: 20)),
SizedBox(height: 15)
])
]);
}
Widget paymentPopup(context) {
return AlertDialog(
content: Stack(
overflow: Overflow.visible,
children: <Widget>[
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('À:'),
Padding(
padding: EdgeInsets.all(8.0),
child: Text(this._outputPubkey.text,
textAlign: TextAlign.center,
style:
TextStyle(fontSize: 15, fontWeight: FontWeight.w500)),
),
SizedBox(height: 20),
Text('Montant (Ğ1):'),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
textAlign: TextAlign.center,
autofocus: true,
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'(^\d*\.?\d*)'))
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
child: Text("Payer"),
color: Color(0xffFFD68E),
onPressed: () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
}
},
),
)
],
),
),
],
),
);
}
}

101
lib/screens/home.dart Normal file
View File

@ -0,0 +1,101 @@
import 'package:gecko/globals.dart';
import 'package:gecko/models/home.dart';
import 'package:gecko/screens/history.dart';
import 'package:flutter/material.dart';
import 'dart:ui';
import 'package:gecko/screens/myWallets/walletsHome.dart';
import 'package:gecko/screens/settings.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class HomeScreen extends StatelessWidget {
var currentTab = [HistoryScreen(), WalletsHome()];
@override
Widget build(BuildContext context) {
HomeProvider _homeProvider = Provider.of<HomeProvider>(context);
return Scaffold(
drawer: Drawer(
child: Column(
children: <Widget>[
Expanded(
child: ListView(padding: EdgeInsets.zero, children: <Widget>[
DrawerHeader(
child: Column(children: <Widget>[
SizedBox(height: 0),
Image(
image: AssetImage('assets/icon/gecko_final.png'),
height: 130),
]),
decoration: BoxDecoration(
color: Color(0xffD28928),
),
),
ListTile(
title: Text('Paramètres'),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return SettingsScreen();
}),
);
},
),
ListTile(
title: Text('A propos'),
onTap: () {
// Update the state of the app.
// ...
},
),
])),
Container(
child: Align(
alignment: FractionalOffset.bottomCenter,
child: Text('Ğecko v$appVersion'))),
SizedBox(height: 20)
],
),
),
appBar: AppBar(
leading: Builder(
builder: (context) => IconButton(
icon: new Icon(Icons.menu, color: Colors.grey[850]),
onPressed: () => Scaffold.of(context).openDrawer(),
)),
title: Text('Ğecko', style: TextStyle(color: Colors.grey[850])),
actions: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.search, color: Colors.grey[850]),
),
],
backgroundColor: Color(0xffFFD58D),
),
backgroundColor: Color(0xffF9F9F1),
body: currentTab[_homeProvider.currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Color(0xffFFD58D),
fixedColor: Colors.grey[850],
unselectedItemColor: Color(0xffBD935C),
type: BottomNavigationBarType.fixed,
onTap: (index) {
_homeProvider.currentIndex = index;
},
currentIndex: _homeProvider.currentIndex,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.format_list_bulleted),
label: 'Accueil',
),
BottomNavigationBarItem(
icon: new Icon(Icons.lock),
label: 'Mes portefeuilles',
)
],
),
);
}
}

View File

@ -0,0 +1,84 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'package:gecko/models/walletOptions.dart';
import 'dart:io';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class ChangePinScreen extends StatelessWidget with ChangeNotifier {
ChangePinScreen(
{Key keyMyWallets, @required this.walletName, @required this.oldPin})
: super(key: keyMyWallets);
final String walletName;
final oldPin;
Directory appPath;
NewWallet _newWalletFile;
@override
Widget build(BuildContext context) {
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context);
_walletOptions.changePin(walletName, oldPin);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: SizedBox(
height: 22,
child: Text(walletName),
)),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 80),
Text(
'Choisissez un code secret autogénéré :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(height: 30),
Container(
child: Stack(
alignment: Alignment.centerRight,
children: <Widget>[
TextField(
enabled: true,
controller: _walletOptions.newPin,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
IconButton(
icon: Icon(Icons.replay),
color: Color(0xffD28928),
onPressed: () async {
_newWalletFile =
await _walletOptions.changePin(walletName, oldPin);
},
),
],
),
),
SizedBox(height: 30),
SizedBox(
width: 200,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 12,
primary: Colors.green[400], //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => _walletOptions.storeWallet(
context, walletName, _newWalletFile),
child: Text('Confirmer', style: TextStyle(fontSize: 28))),
)
]))));
}
}

View File

@ -0,0 +1,175 @@
import 'package:dubp/dubp.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gecko/models/generateWallets.dart';
import 'package:gecko/models/myWallets.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class ConfirmStoreWallet extends StatelessWidget with ChangeNotifier {
ConfirmStoreWallet({
Key validationKey,
@required this.generatedMnemonic,
@required this.generatedWallet,
}) : super(key: validationKey);
String generatedMnemonic;
NewWallet generatedWallet;
TextEditingController _mnemonicController = TextEditingController();
TextEditingController _pubkey = TextEditingController();
TextEditingController _inputRestoreWord = TextEditingController();
TextEditingController walletName = TextEditingController();
FocusNode _wordFocus = FocusNode();
@override
Widget build(BuildContext context) {
GenerateWalletsProvider _generateWalletProvider =
Provider.of<GenerateWalletsProvider>(context);
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
this._mnemonicController.text = generatedMnemonic;
this._pubkey.text = generatedWallet.publicKey;
return WillPopScope(
onWillPop: () {
_generateWalletProvider.isAskedWordValid = false;
_generateWalletProvider.askedWordColor = Colors.black;
return Future<bool>.value(true);
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
Navigator.of(context).pop();
_generateWalletProvider.isAskedWordValid = false;
_generateWalletProvider.askedWordColor = Colors.black;
}),
title: SizedBox(
height: 22,
child: Text('Confirmez ce portefeuille'),
)),
body: Center(
child: Column(children: <Widget>[
SizedBox(height: 15),
Text(
'Votre clé publique est :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextField(
enabled: false,
controller: this._pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text(
'Quel est le ${_generateWalletProvider.nbrWord + 1}ème mot de votre phrase de restauration ?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextFormField(
focusNode: _wordFocus,
autofocus: true,
enabled: !_generateWalletProvider.isAskedWordValid,
controller: this._inputRestoreWord,
textInputAction: TextInputAction.next,
onChanged: (value) {
_generateWalletProvider.checkAskedWord(
value, _mnemonicController.text);
},
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: _generateWalletProvider.askedWordColor,
fontWeight: FontWeight.w500)),
SizedBox(height: 12),
Text(
'Choisissez un nom pour votre portefeuille :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextFormField(
focusNode: _generateWalletProvider.walletNameFocus,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp('[A-Za-z|0-9|\\-|_| ]')),
],
controller: this.walletName,
textInputAction: TextInputAction.next,
onChanged: (v) {
_generateWalletProvider.nameChanged();
},
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
fontWeight: FontWeight.w500)),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
width: 200,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 12,
primary: Colors.green[
400], //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: (_generateWalletProvider
.isAskedWordValid &&
this.walletName.text != '')
? () async {
await _generateWalletProvider.storeWallet(
generatedWallet,
walletName.text,
context);
_generateWalletProvider.isAskedWordValid =
false;
_generateWalletProvider.askedWordColor =
Colors.black;
WidgetsBinding.instance
.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider
.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
}
: null,
child: Text('Confirmer',
style: TextStyle(fontSize: 28))),
))),
SizedBox(height: 70),
Text('TRICHE PENDANT ALPHA: ' + this._mnemonicController.text,
style: TextStyle(
fontSize: 10.0,
color: Colors.black,
fontWeight: FontWeight.normal)),
]),
),
));
}
}

View File

@ -0,0 +1,201 @@
import 'package:gecko/models/generateWallets.dart';
import 'package:gecko/screens/myWallets/confirmWalletStorage.dart';
import 'package:flutter/material.dart';
import 'package:printing/printing.dart';
import 'package:provider/provider.dart';
import 'package:super_tooltip/super_tooltip.dart';
// ignore: must_be_immutable
class GenerateWalletsScreen extends StatelessWidget {
SuperTooltip tooltip;
bool hasError = false;
String validPin = 'NO PIN';
String currentText = "";
var pinColor = Colors.grey[300];
GlobalKey _toolTipPubkey = GlobalKey();
GlobalKey _toolTipSentence = GlobalKey();
GlobalKey _toolTipSecret = GlobalKey();
@override
Widget build(BuildContext context) {
GenerateWalletsProvider _generateWalletProvider =
Provider.of<GenerateWalletsProvider>(context);
_generateWalletProvider.generateMnemonic();
print('IS GENERATED ? : ' +
_generateWalletProvider.walletIsGenerated.toString());
return Scaffold(
appBar: AppBar(
title: SizedBox(
height: 22,
child: Text('Générer un portefeuille'),
)),
floatingActionButton: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
heroTag: "buttonGenerateWallet",
onPressed: () => _generateWalletProvider.generateMnemonic(),
child: Container(
height: 40.0,
width: 40.0,
child: Icon(Icons.replay, color: Colors.grey[850]),
),
backgroundColor: Color(
0xffEFEFBF), //Color(0xffFFD68E), //Color.fromARGB(500, 204, 255, 255),
))),
body: SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 20),
toolTips(_toolTipPubkey, 'Clé publique:',
"C'est votre RIB en Ğ1, les gens l'utiliseront pour vous payer"),
TextField(
enabled: false,
controller: _generateWalletProvider.pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
SizedBox(height: 8),
toolTips(_toolTipSentence, 'Phrase de restauration:',
"Notez et gardez cette phrase précieusement sur un papier, elle vous servira à restaurer votre portefeuille sur un autre appareil"),
TextField(
enabled: false,
controller: _generateWalletProvider.mnemonicController,
maxLines: 3,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
),
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.w400)),
SizedBox(height: 8),
toolTips(_toolTipSecret, 'Code secret:',
"Retenez bien votre code secret, il vous sera demandé à chaque paiement, ainsi que pour configurer votre portefeuille"),
Container(
child: Stack(
alignment: Alignment.centerRight,
children: <Widget>[
TextField(
enabled: false,
controller: _generateWalletProvider.pin,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
IconButton(
icon: Icon(Icons.replay),
color: Color(0xffD28928),
onPressed: () {
_generateWalletProvider.changePinCode();
},
),
],
),
),
SizedBox(height: 20),
new ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: _generateWalletProvider.walletIsGenerated
? () {
_generateWalletProvider.nbrWord =
_generateWalletProvider.getRandomInt();
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmStoreWallet(
generatedMnemonic:
_generateWalletProvider.generatedMnemonic,
generatedWallet:
_generateWalletProvider.actualWallet);
}),
);
}
: null,
child: Text('Enregistrer ce portefeuille',
style: TextStyle(fontSize: 20))),
SizedBox(height: 20),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return PrintWallet(
_generateWalletProvider.generatedMnemonic,
_generateWalletProvider.actualWallet.publicKey);
}),
);
},
child: Icon(Icons.print))
]),
));
}
Widget toolTips(_key, _text, _message) {
return GestureDetector(
onTap: () {
final dynamic _toolTip = _key.currentState;
_toolTip.ensureTooltipVisible();
},
child: Tooltip(
padding: EdgeInsets.all(10),
key: _key,
showDuration: Duration(seconds: 5),
message: _message,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(children: <Widget>[
SizedBox(
width: 30,
height: 25,
child: Icon(Icons.info_outline,
size: 22, color: Color(0xffD28928))),
SizedBox(height: 1)
]),
Text(
_text,
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(width: 45)
])));
}
}
// ignore: must_be_immutable
class PrintWallet extends StatelessWidget {
PrintWallet(this.sentence, this.pubkey);
final String sentence;
final String pubkey;
@override
Widget build(BuildContext context) {
GenerateWalletsProvider _generateWalletProvider =
Provider.of<GenerateWalletsProvider>(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Imprimer ce portefeuille')),
body: PdfPreview(
build: (format) =>
_generateWalletProvider.printWallet(sentence, pubkey),
),
),
);
}
}

View File

@ -0,0 +1,252 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'package:gecko/models/myWallets.dart';
import 'package:gecko/models/walletOptions.dart';
import 'package:gecko/screens/myWallets/changePin.dart';
import 'dart:async';
import 'package:pin_code_fields/pin_code_fields.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class WalletOptions extends StatelessWidget with ChangeNotifier {
WalletOptions({Key keyMyWallets, @required this.walletName})
: super(key: keyMyWallets);
String walletName;
StreamController<ErrorAnimationType> errorController;
TextEditingController _enterPin = new TextEditingController();
final formKey = GlobalKey<FormState>();
bool hasError = false;
var pinColor = Color(0xffF9F9F1);
var walletPin = '';
Future<NewWallet> get badWallet => null;
@override
Widget build(BuildContext context) {
print("Build walletOptions");
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context);
MyWalletsProvider _myWalletProvider =
Provider.of<MyWalletsProvider>(context);
errorController = StreamController<ErrorAnimationType>();
// _walletOptions.isWalletUnlock = false;
return WillPopScope(
onWillPop: () {
_walletOptions.isWalletUnlock = false;
return Future<bool>.value(true);
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.black),
onPressed: () {
Navigator.of(context).pop();
_walletOptions.isWalletUnlock = false;
}),
title: SizedBox(
height: 22,
child: Text(walletName),
)),
body: Center(
child: SafeArea(
child: Column(children: <Widget>[
Visibility(
visible: _walletOptions.isWalletUnlock,
child: Expanded(
child: Column(children: <Widget>[
SizedBox(height: 15),
Text(
'Clé publique:',
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
SizedBox(height: 15),
Text(
_walletOptions.pubkey.text,
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold),
),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
primary: Color(
0xffFFD68E), //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => _walletOptions
.renameWalletAlerte(
context, walletName)
.then((_result) {
if (_result == true) {
WidgetsBinding.instance
.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider
.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
}
}),
child: Text('Renommer ce portefeuille',
style: TextStyle(fontSize: 20)))))),
SizedBox(height: 30),
SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
primary: Color(
0xffFFD68E), //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () {
// changePin(widget.walletName, this.walletPin);
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ChangePinScreen(
walletName: walletName,
oldPin: this.walletPin);
}),
);
},
child: Text('Changer mon code secret',
style: TextStyle(fontSize: 20)))),
SizedBox(height: 30),
SizedBox(
height: 50,
width: 300,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 6,
primary: Colors
.redAccent, //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () async {
await _walletOptions.deleteWallet(
context, walletName);
WidgetsBinding.instance.addPostFrameCallback((_) {
_myWalletProvider.listWallets =
_myWalletProvider.getAllWalletsNames();
_myWalletProvider.rebuildWidget();
});
},
child: Text('Supprimer ce portefeuille',
style: TextStyle(fontSize: 20)))),
SizedBox(height: 50),
Text(
'Portefeuille déverrouillé',
style: TextStyle(
color: Colors.green,
fontWeight: FontWeight.w700,
fontSize: 15),
),
SizedBox(height: 10)
]))),
Visibility(
visible: !_walletOptions.isWalletUnlock,
child: Expanded(
child: Column(children: <Widget>[
SizedBox(height: 80),
Text(
'Veuillez tapper votre code secret pour dévérouiller votre portefeuille.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontWeight: FontWeight.w400),
),
SizedBox(height: 50),
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 30),
child: PinCodeTextField(
autoFocus: true,
appContext: context,
pastedTextStyle: TextStyle(
color: Colors.green.shade600,
fontWeight: FontWeight.bold,
),
length: 6,
obscureText: false,
obscuringCharacter: '*',
animationType: AnimationType.fade,
validator: (v) {
if (v.length < 6) {
return "Votre code PIN fait 6 caractères";
} else {
return null;
}
},
pinTheme: PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: 60,
fieldWidth: 50,
activeFillColor:
hasError ? Colors.orange : Colors.white,
),
cursorColor: Colors.black,
animationDuration: Duration(milliseconds: 300),
textStyle: TextStyle(fontSize: 20, height: 1.6),
backgroundColor: pinColor,
enableActiveFill: false,
errorAnimationController: errorController,
controller: _enterPin,
keyboardType: TextInputType.text,
boxShadows: [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
)
],
onCompleted: (_pin) async {
print("Completed");
final resultWallet =
await _walletOptions.readLocalWallet(
this.walletName, _pin.toUpperCase());
if (resultWallet == 'bad') {
errorController.add(ErrorAnimationType
.shake); // Triggering error shake animation
hasError = true;
pinColor = Colors.red[200];
notifyListeners();
} else {
pinColor = Colors.green[200];
// setState(() {});
// await Future.delayed(Duration(milliseconds: 50));
this.walletPin = _pin.toUpperCase();
// isWalletUnlock = true;
notifyListeners();
}
},
onChanged: (value) {
if (pinColor != Color(0xffF9F9F1)) {
pinColor = Color(0xffF9F9F1);
}
print(value);
},
)),
)
]))),
])))));
}
}

View File

@ -0,0 +1,124 @@
import 'package:gecko/models/myWallets.dart';
import 'package:gecko/models/walletOptions.dart';
import 'package:gecko/screens/myWallets/generateWallets.dart';
import 'package:flutter/material.dart';
import 'package:gecko/screens/myWallets/walletOptions.dart';
import 'package:provider/provider.dart';
// ignore: must_be_immutable
class WalletsHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
WalletOptionsProvider _walletOptions =
Provider.of<WalletOptionsProvider>(context);
print('BUILD: WalletsHome');
_walletOptions.isWalletUnlock = false;
myWalletProvider.listWallets = myWalletProvider.getAllWalletsNames();
final bool isWalletsExists = myWalletProvider.checkIfWalletExist();
return Scaffold(
floatingActionButton: Visibility(
visible: (isWalletsExists),
child: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
heroTag: "buttonGenerateWallet",
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return GenerateWalletsScreen();
}),
);
},
child: Container(
height: 40.0,
width: 40.0,
child: Icon(Icons.person_add_alt_1_rounded,
color: Colors.grey[850])),
backgroundColor: Color(0xffEFEFBF))))),
body: SafeArea(
child: Column(children: <Widget>[
Visibility(
visible: (!isWalletsExists),
child: Column(children: <Widget>[
SizedBox(height: 120),
Center(
child: Text("Vous n'avez encore généré aucun portefeuille.",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.w500),
textAlign: TextAlign.center)),
SizedBox(height: 80),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return GenerateWalletsScreen();
}),
),
child: Text('Générer un portefeuille',
style: TextStyle(fontSize: 20))),
SizedBox(height: 15),
Center(
child: Text("ou",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.w500),
textAlign: TextAlign.center)),
SizedBox(height: 15),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => myWalletProvider.importWallet(),
child: Text('Importer un portefeuille existant',
style: TextStyle(fontSize: 20))),
])),
Visibility(visible: isWalletsExists, child: myWalletsList(context))
])));
}
Widget myWalletsList(BuildContext context) {
MyWalletsProvider myWalletProvider =
Provider.of<MyWalletsProvider>(context);
// TODO: Show history of my wallets
// HistoryProvider _historyProvider = Provider.of<HistoryProvider>(context);
// Map _balance = Map();
List _listWallets = [];
myWalletProvider.listWallets.forEach((_name, _pubkey) {
_listWallets.add(_name);
// _balance[_name] = _historyProvider.getBalance(_pubkey).toString();
print(_name + _pubkey);
});
return Column(children: <Widget>[
SizedBox(height: 8),
for (String _repository in _listWallets)
ListTile(
contentPadding: const EdgeInsets.all(5.0),
leading: Padding(
padding: const EdgeInsets.all(15.0),
child: Text("0 Ğ1", style: TextStyle(fontSize: 14.0))),
title: Text(_repository, style: TextStyle(fontSize: 16.0)),
subtitle: Text(myWalletProvider.listWallets[_repository],
style: TextStyle(fontSize: 11.0)),
dense: true,
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return WalletOptions(walletName: _repository);
}));
},
)
]);
}
}

55
lib/screens/settings.dart Normal file
View File

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'package:gecko/models/myWallets.dart';
import 'dart:io';
// ignore: must_be_immutable
class SettingsScreen extends StatelessWidget {
String generatedMnemonic;
bool walletIsGenerated = false;
NewWallet actualWallet;
String newWalletName;
bool hasError = false;
String validPin = 'NO PIN';
String currentText = "";
var pinColor = Colors.grey[300];
Directory appPath;
MyWalletsProvider _myWallets = MyWalletsProvider();
@override
Widget build(BuildContext context) {
// getAppDirectory();
return Scaffold(
appBar: AppBar(
title: SizedBox(
height: 22,
child: Text('Paramètres'),
)),
body: Column(children: <Widget>[
SizedBox(height: 20),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 100,
width: 1000,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 5,
primary: Colors
.redAccent, //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => {
print('Suppression de tous les wallets'),
_myWallets.deleteAllWallet(context)
},
child: Text(
"EFFACER TOUS MES PORTEFEUILLES, LE TEMPS DE L'ALPHA",
style: TextStyle(fontSize: 20)))))),
SizedBox(height: 50),
]));
}
}

View File

@ -1,311 +0,0 @@
import 'package:gecko/parsingGVA.dart';
import 'package:gecko/query.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:qrscan/qrscan.dart' as scanner;
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:sentry/sentry.dart' as sentry;
import 'package:truncate/truncate.dart';
//ignore: must_be_immutable
class HistoryScreen extends StatefulWidget {
const HistoryScreen({Key keyHistory}) : super(key: keyHistory);
@override
State<StatefulWidget> createState() => HistoryScreenState();
}
class HistoryScreenState extends State<HistoryScreen> {
Widget currentScreen;
Uint8List bytes = Uint8List(0);
final TextEditingController _outputPubkey = new TextEditingController();
final nRepositories = 20;
// String pubkey = 'D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU'; // For debug
String pubkey = '';
bool isBuilding = true;
ScrollController _scrollController = new ScrollController();
_scrollListener() {
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {
setState(() {
print("reach the bottom");
});
}
}
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
// _scrollController
// ..addListener(() {
// if (_scrollController.position.pixels ==
// _scrollController.position.maxScrollExtent) {
// // print(
// // "DEBUG H fetchMoreCursor in scrollController: $fetchMoreCursor");
// fetchMore(opts);
// }
// });
}
@override
Widget build(BuildContext context) {
print('Build pubkey : ' + pubkey);
print('Build this.pubkey : ' + this.pubkey);
print('isBuilding: ' + isBuilding.toString());
return Scaffold(
floatingActionButton: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
heroTag: "buttonScan",
onPressed: () async {
await scan();
// print(resultScan);
// if (resultScan != 'false') {
// onTabTapped(0);
// }
},
child: Container(
height: 40.0,
width: 40.0,
child: Image.asset('images/scanner.png')),
backgroundColor: Color(
0xffEFEFBF), //Color(0xffFFD68E), //Color.fromARGB(500, 204, 255, 255),
),
),
),
body: Column(children: <Widget>[
SizedBox(height: 8),
TextField(
// Entrée de la pubkey
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: 14),
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: 14.0, fontWeight: FontWeight.bold)),
if (this.pubkey != '') historyQuery(),
]));
}
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) {
print('Error GVA: ' + result.exception.toString());
return Column(children: <Widget>[
SizedBox(height: 50),
Text(
"Aucun noeud GVA valide n'a pu être trouvé.\nVeuillez réessayer ultérieurement.",
style: TextStyle(fontSize: 17.0),
)
]);
}
if (result.data == null && result.exception.toString() == null) {
return const Text('Aucune donnée à afficher.');
}
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'];
final num balance =
removeDecimalZero(result.data['balance']['amount'] / 100);
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);
// }
// }
// });
// s/o : https://stackoverflow.com/questions/54065354/how-to-detect-scroll-position-of-listview-in-flutter/54188385#54188385
// new NotificationListener(
// child: new ListView(
// controller: _scrollController,
// ),
// onNotification: (t) {
// if (t is ScrollEndNotification) {
// fetchMore(opts);
// }
// },
// );
// fetchMore(opts);
print(
"###### DEBUG H Parse blockchainTX list. Cursor: $fetchMoreCursor ######");
List _transBC = parseHistory(blockchainTX);
// Build history list
return Expanded(
child: ListView(
controller: _scrollController,
children: <Widget>[
SizedBox(height: 7),
if (this.pubkey != '')
Text(balance.toString() + ' Ğ1',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30.0)),
SizedBox(height: 12),
for (var repository in _transBC)
ListTile(
contentPadding: const EdgeInsets.all(5.0),
leading:
Text(repository[3], style: TextStyle(fontSize: 14.0)),
title: Text(
repository[1].toString() +
'\n' +
truncate(repository[2], 17,
omission: "...",
position: TruncatePosition.end),
style: TextStyle(fontSize: 14.0)),
subtitle:
Text(repository[5], style: TextStyle(fontSize: 14.0)),
dense: true,
onTap: () {
isPubkey(repository[2]);
}),
if (result.isLoading)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
],
),
],
));
},
),
],
));
}
Future scan() async {
await Permission.camera.request();
String barcode;
try {
barcode = await scanner.scan();
} catch (e, stack) {
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
return 'false';
}
// this._outputPubkey.text = "";
if (barcode != null) {
this._outputPubkey.text = barcode;
isPubkey(barcode);
} else {
return 'false';
}
return barcode;
}
String isPubkey(pubkey) {
final 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 !!!");
setState(() {
this.pubkey = pubkey;
this._outputPubkey.text = pubkey;
});
// setState(() {
// this._outputBalance.text = balance.toString();
// });
return pubkey;
}
return '';
}
num removeDecimalZero(double n) {
String result = n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 1);
return num.parse(result);
}
}

View File

@ -1,66 +0,0 @@
import 'package:gecko/ui/historyScreen.dart';
import 'package:flutter/material.dart';
import 'dart:typed_data';
import 'dart:ui';
import 'package:gecko/ui/myWallets/walletsHome.dart';
//ignore: must_be_immutable
class HomeScreen extends StatefulWidget {
HomeScreen({this.screens});
final List<Widget> screens;
@override
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
GlobalKey<HistoryScreenState> _keyHistory = GlobalKey();
int currentIndex = 0;
Widget currentScreen;
void onTabTapped(int index) {
setState(() {
currentIndex = index;
});
}
Uint8List bytes = Uint8List(0);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xffF9F9F1),
body: SafeArea(
child: IndexedStack(
index: currentIndex,
children: <Widget>[
HistoryScreen(
keyHistory: _keyHistory,
),
WalletsHome(),
],
),
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Color(0xffFFD68E),
fixedColor: Color(0xff855F2D),
unselectedItemColor: Color(0xffBD935C),
type: BottomNavigationBarType.fixed,
onTap: onTabTapped,
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons
.format_list_bulleted), //Icons.person_add_alt_1_rounded //Icons.lock
label: 'Accueil',
),
BottomNavigationBarItem(
icon: new Icon(Icons.lock),
label: 'Mes portefeuilles',
)
],
),
);
}
}

View File

@ -1,209 +0,0 @@
import 'dart:io';
import 'dart:math';
import 'package:dubp/dubp.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class ConfirmStoreWallet extends StatefulWidget {
final String generatedMnemonic;
final NewWallet generatedWallet;
ConfirmStoreWallet(
{Key validationKey,
@required this.generatedMnemonic,
@required this.generatedWallet})
: super(key: validationKey);
@override
ConfirmStoreWalletState createState() => ConfirmStoreWalletState();
}
class ConfirmStoreWalletState extends State<ConfirmStoreWallet> {
// GlobalKey<ValidStoreWalletState> _keyValidWallets = GlobalKey();
void initState() {
super.initState();
// DubpRust.setup();
this._mnemonicController.text = widget.generatedMnemonic;
this._pubkey.text = widget.generatedWallet.publicKey;
nbrWord = getRandomInt();
}
TextEditingController _mnemonicController = new TextEditingController();
TextEditingController _pubkey = new TextEditingController();
TextEditingController _pin = new TextEditingController();
TextEditingController _inputRestoreWord = new TextEditingController();
TextEditingController walletName = new TextEditingController();
// List _listWallets = [];
int nbrWord;
bool isAskedWordValid = false;
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(),
body: Center(
child: Column(children: <Widget>[
SizedBox(height: 15),
Text(
'Votre clé publique est :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextField(
enabled: false,
controller: this._pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
SizedBox(height: 12),
Text(
'Quel est le ${nbrWord + 1}ème mot de votre phrase de restauration ?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextField(
enabled: !isAskedWordValid,
controller: this._inputRestoreWord,
onChanged: (value) {
checkAskedWord(value);
},
maxLines: 2,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
fontWeight: FontWeight.w500)),
SizedBox(height: 12),
Text(
'Choisissez un nom pour votre portefeuille :',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
TextField(
enabled: isAskedWordValid,
controller: this.walletName,
onChanged: (v) {
nameChanged();
},
maxLines: 2,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
fontWeight: FontWeight.w500)),
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
width: 200,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 12,
primary: Colors
.green[400], //Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed:
(isAskedWordValid && this.walletName.text != '')
? () => storeWallet()
: null,
child:
Text('Confirmer', style: TextStyle(fontSize: 28))),
))),
SizedBox(height: 70),
Text('TRICHE PENDANT ALPHA: ' + this._mnemonicController.text,
style: TextStyle(
fontSize: 10.0,
color: Colors.black,
fontWeight: FontWeight.normal)),
]),
),
);
}
Future storeWallet() async {
final appPath = await _localPath;
final walletFile =
// File('$appPath/wallets/${this.walletName.text}/wallet.dewif');
File('$appPath/wallets/MonWallet/wallet.dewif');
// TODO: Use custom wallet name for storage
final isExist = await Directory('$appPath/wallets').exists();
if (isExist == false) {
new Directory('$appPath/wallets').createSync();
}
new Directory('$appPath/wallets/${this.walletName.text}').createSync();
walletFile.writeAsString('${widget.generatedWallet.dewif}');
_pin.clear();
// await getAllWalletsNames();
Navigator.pop(context, true);
Navigator.pop(context, this._pubkey.text);
// setState(() {});
// FocusScope.of(context).unfocus();
return this.walletName.text;
}
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
// Future<List> getAllWalletsNames() async {
// final _appPath = await getApplicationDocumentsDirectory();
// // List _listWallets = [];
// // _listWallets.add('tortuuue');
// this._listWallets.clear();
// print(_appPath);
// _appPath
// .list(recursive: false, followLinks: false)
// .listen((FileSystemEntity entity) {
// print(entity.path.split('/').last);
// this._listWallets.add(entity.path.split('/').last);
// });
// return _listWallets;
// // final _local = await _appPath.path.list().toList();
// }
void checkAskedWord(value) {
print(this._mnemonicController.text.split(' ')[nbrWord]);
print(value);
if (this._mnemonicController.text.split(' ')[nbrWord] == value) {
print('Word is OK');
isAskedWordValid = true;
} else {
isAskedWordValid = false;
}
setState(() {});
}
int getRandomInt() {
var rng = new Random();
return rng.nextInt(12);
}
void nameChanged() {
setState(() {});
}
}

View File

@ -1,233 +0,0 @@
import 'package:gecko/ui/myWallets/confirmWalletStorage.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sentry/sentry.dart' as sentry;
import 'package:dubp/dubp.dart';
import 'package:super_tooltip/super_tooltip.dart';
class GenerateWalletsScreen extends StatefulWidget {
const GenerateWalletsScreen({Key keyGenWallet}) : super(key: keyGenWallet);
@override
GenerateWalletsState createState() => GenerateWalletsState();
}
class GenerateWalletsState extends State<GenerateWalletsScreen> {
// GlobalKey<MyWalletState> _keyMyWallets = GlobalKey();
// GlobalKey<ValidStoreWalletState> _keyValidWallets = GlobalKey();
void initState() {
super.initState();
DubpRust.setup();
generateMnemonic();
}
TextEditingController _mnemonicController = new TextEditingController();
TextEditingController _pubkey = new TextEditingController();
TextEditingController _pin = new TextEditingController();
String generatedMnemonic;
bool walletIsGenerated = false;
NewWallet actualWallet;
SuperTooltip tooltip;
// final formKey = GlobalKey<FormState>();
bool hasError = false;
String validPin = 'NO PIN';
String currentText = "";
var pinColor = Colors.grey[300];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
heroTag: "buttonGenerateWallet",
onPressed: () => generateMnemonic(),
// print(resultScan);
// if (resultScan != 'false') {
// onTabTapped(0);
// }
child: Container(
height: 40.0, width: 40.0, child: Icon(Icons.replay)),
backgroundColor: Color(
0xffEFEFBF), //Color(0xffFFD68E), //Color.fromARGB(500, 204, 255, 255),
))),
body: SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 20),
Tooltip(
message:
"C'est votre RIB en Ğ1, les gens l'utiliseront pour vous payer",
child: Text(
'Clé publique:',
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
),
TextField(
enabled: false,
controller: this._pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Tooltip(
message:
"Notez et gardez cette phrase précieusement sur un papier, elle vous servira à restaurer votre portefeuille sur un autre appareil",
child: Text(
'Phrase de restauration:',
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
),
TextField(
enabled: false,
controller: this._mnemonicController,
maxLines: 3,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(15.0),
),
style: TextStyle(
fontSize: 22.0,
color: Colors.black,
fontWeight: FontWeight.w400)),
SizedBox(height: 8),
Tooltip(
message:
"Retenez bien votre code secret, il vous sera demandé à chaque paiement, ainsi que pour configurer votre portefeuille",
child: Text(
'Code secret:',
style: TextStyle(
fontSize: 15.0,
color: Colors.grey[600],
fontWeight: FontWeight.w400),
),
),
Container(
child: Stack(
alignment: Alignment.centerRight,
children: <Widget>[
TextField(
enabled: false,
controller: this._pin,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 30.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
IconButton(
icon: Icon(Icons.replay),
color: Color(0xffD28928),
onPressed: () {
changePinCode();
},
),
],
),
),
SizedBox(height: 20),
// Expanded(child: Align(alignment: Alignment.bottomCenter)),
new ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: walletIsGenerated
? () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ConfirmStoreWallet(
// validationKey: _keyValidWallets,
generatedMnemonic: this.generatedMnemonic,
generatedWallet: this.actualWallet);
}),
)
// .then((value) => setState(() {
// if (value != null) {
// _pin.clear();
// _mnemonicController.clear();
// _pubkey.clear();
// this.generatedMnemonic = null;
// this.actualWallet = null;
// this.walletIsGenerated = false;
// }
// }))
;
}
: null,
child: Text('Enregistrer ce portefeuille',
style: TextStyle(fontSize: 20))),
SizedBox(height: 20)
]),
));
}
Future<String> generateMnemonic() async {
try {
this.generatedMnemonic =
await DubpRust.genMnemonic(language: Language.french);
this.actualWallet = await generateWallet(this.generatedMnemonic);
this.walletIsGenerated = true;
} catch (e, stack) {
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
}
// await checkIfWalletExist();
return this.generatedMnemonic;
}
Future<NewWallet> generateWallet(generatedMnemonic) async {
try {
this.actualWallet = await DubpRust.genWalletFromMnemonic(
language: Language.french,
mnemonic: generatedMnemonic,
secretCodeType: SecretCodeType.letters);
} catch (e, stack) {
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
}
setState(() {
this._mnemonicController.text = generatedMnemonic;
this._pubkey.text = actualWallet.publicKey;
this._pin.text = actualWallet.pin;
});
return actualWallet;
}
Future<void> changePinCode() async {
this.actualWallet = await DubpRust.changeDewifPin(
dewif: this.actualWallet.dewif,
oldPin: this.actualWallet.pin,
);
setState(() {
this._pin.text = actualWallet.pin;
});
}
}

View File

@ -1,293 +0,0 @@
// import 'package:gecko/ui/generateWallets.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'package:sentry/sentry.dart' as sentry;
import 'dart:io';
import 'dart:async';
import 'package:path_provider/path_provider.dart';
import 'package:pin_code_fields/pin_code_fields.dart';
class MyWalletsScreen extends StatefulWidget {
const MyWalletsScreen({Key keyMyWallets}) : super(key: keyMyWallets);
@override
MyWalletState createState() => MyWalletState();
}
class MyWalletState extends State<MyWalletsScreen> {
// GlobalKey<GenerateWalletState> _keyGenWallet = GlobalKey();
StreamController<ErrorAnimationType> errorController;
Directory appPath;
List _listWallets = [];
void initState() {
super.initState();
errorController = StreamController<ErrorAnimationType>();
initAppDirectory();
DubpRust.setup();
// getAllWalletsNames();
// initAppDirectory();
// _walletsList = await getAllWalletsNames();
// HistoryScreen(
// keyHistory: _keyHistory,
// );
}
void initAppDirectory() async {
appPath = await getApplicationDocumentsDirectory();
appPath = Directory('${appPath.path}/wallets');
_listWallets = getAllWalletsNames();
}
TextEditingController _pubkey = new TextEditingController();
TextEditingController _enterPin = new TextEditingController();
final formKey = GlobalKey<FormState>();
bool hasError = false;
String validPin = 'NO PIN';
String currentText = "";
var pinColor = Color(0xffF9F9F1);
@override
Widget build(BuildContext context) {
// final _walletsList = getAllWalletsNames();
return SafeArea(
child: Column(children: <Widget>[
SizedBox(height: 8),
for (var repository in this._listWallets)
ListTile(
contentPadding: const EdgeInsets.all(5.0),
leading: Text(repository, style: TextStyle(fontSize: 14.0)),
title: Text(repository, style: TextStyle(fontSize: 14.0)),
subtitle: Text(repository, style: TextStyle(fontSize: 14.0)),
dense: true,
onTap: () {
openWalletOptions(repository);
}),
InkWell(
child: TextField(
enabled: false,
controller: this._pubkey,
maxLines: 1,
textAlign: TextAlign.center,
decoration: InputDecoration(),
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
onTap: () {
print("Ma pubkey click");
// _keyHistory.currentState.scan();
},
),
SizedBox(height: 12),
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 30),
child: PinCodeTextField(
appContext: context,
pastedTextStyle: TextStyle(
color: Colors.green.shade600,
fontWeight: FontWeight.bold,
),
length: 6,
obscureText: false,
obscuringCharacter: '*',
animationType: AnimationType.fade,
validator: (v) {
if (v.length < 6) {
return "Votre code PIN fait 6 caractères";
} else {
return null;
}
},
pinTheme: PinTheme(
shape: PinCodeFieldShape.box,
borderRadius: BorderRadius.circular(5),
fieldHeight: 60,
fieldWidth: 50,
activeFillColor: hasError ? Colors.orange : Colors.white,
),
cursorColor: Colors.black,
animationDuration: Duration(milliseconds: 300),
textStyle: TextStyle(fontSize: 20, height: 1.6),
backgroundColor: pinColor,
enableActiveFill: false,
errorAnimationController: errorController,
controller: _enterPin,
keyboardType: TextInputType.text,
boxShadows: [
BoxShadow(
offset: Offset(0, 1),
color: Colors.black12,
blurRadius: 10,
)
],
onCompleted: (v) async {
print("Completed");
final resultWallet = await readLocalWallet(v.toUpperCase());
if (resultWallet == 'bad') {
errorController.add(ErrorAnimationType
.shake); // Triggering error shake animation
setState(() {
hasError = true;
pinColor = Colors.red[200];
});
} else {
setState(() {
pinColor = Colors.green[200];
});
}
},
onChanged: (value) {
if (pinColor != Color(0xffF9F9F1)) {
setState(() {
pinColor = Color(0xffF9F9F1);
});
}
print(value);
},
)),
)
]));
}
Future getPubkeyFromDewif(_dewif, _pin) async {
String _pubkey;
RegExp regExp = new RegExp(
r'^[A-Z0-9]+$',
caseSensitive: false,
multiLine: false,
);
if (regExp.hasMatch(_pin) == true && _pin.length == 6) {
print("Le format du code PIN est correct.");
} else {
print('Format de code PIN invalide');
return 'false';
}
try {
_pubkey = await DubpRust.getDewifPublicKey(dewif: _dewif, pin: _pin);
setState(() {
this._pubkey.text = _pubkey;
});
return _pubkey;
} catch (e, stack) {
print('Bad PIN code !');
print(e);
if (kReleaseMode) {
await sentry.Sentry.captureException(
e,
stackTrace: stack,
);
}
return 'false';
}
}
// Future<bool> checkIfWalletExist(_name) async {
// final appPath = await _localPath;
// final _walletFile = File('$appPath/$_name/wallet.dewif');
// // deleteWallet();
// print(_walletFile.path);
// final isExist = await File(_walletFile.path).exists();
// print('Wallet existe ? : ' + isExist.toString());
// print('Is wallet generated ? : ' + walletIsGenerated.toString());
// if (isExist == true) {
// print('Un wallet existe !');
// return true;
// } else {
// return false;
// }
// }
List getAllWalletsNames() {
// final _appPath = await getApplicationDocumentsDirectory();
// List _listWallets = [];
// _listWallets.add('tortuuue');
this._listWallets.clear();
print(this.appPath);
this
.appPath
.list(recursive: false, followLinks: false)
.listen((FileSystemEntity entity) {
print(entity.path.split('/').last);
this._listWallets.add(entity.path.split('/').last);
});
print('Mes wallets: ');
print(_listWallets);
return _listWallets;
// final _local = await _appPath.path.list().toList();
}
Future openWalletOptions(_name) async {
deleteWallet(_name);
// getAllWalletsNames();
// setState(() {});
// GenerateWalletScreen(keyGenWallet: _keyGenWallet);
// _keyGenWallet.currentState.setState(() {
// getAllWalletsNames();
// });
}
Future readLocalWallet(String _pin) async {
// print(pin);
try {
final file = await _localWallet('this.walletName');
String _localDewif = await file.readAsString();
String _localPubkey;
if ((_localPubkey = await getPubkeyFromDewif(_localDewif, _pin)) !=
'false') {
setState(() {
this._pubkey.text = _localPubkey;
});
return _localDewif;
} else {
throw 'Bad pubkey';
}
} catch (e) {
print('ERROR READING FILE: $e');
setState(() {
this._pubkey.clear();
});
return 'bad';
}
}
Future<int> deleteWallet(_name) async {
try {
final appPath = await _localPath;
final _walletFile = File('$appPath/wallets/$_name/wallet.dewif');
_walletFile.delete();
getAllWalletsNames();
setState(() {
// getAllWalletsNames();
});
return 0;
} catch (e) {
getAllWalletsNames();
setState(() {
// getAllWalletsNames();
});
return 1;
}
}
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> _localWallet(_name) async {
final path = await _localPath;
return File('$path/wallets/$_name/wallet.dewif');
}
}

View File

@ -1,154 +0,0 @@
import 'package:gecko/ui/myWallets/generateWalletsScreen.dart';
import 'package:gecko/ui/myWallets/myWalletsList.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:dubp/dubp.dart';
import 'dart:io';
import 'dart:async';
import 'package:path_provider/path_provider.dart';
class WalletsHome extends StatefulWidget {
const WalletsHome({Key keyGenWallet}) : super(key: keyGenWallet);
@override
WalletsHomeState createState() => WalletsHomeState();
}
class WalletsHomeState extends State<WalletsHome> {
GlobalKey<WalletsHomeState> _keyWalletsHome = GlobalKey();
// GlobalKey<MyWalletState> _keyMyWallets = GlobalKey();
// GlobalKey<ValidStoreWalletState> _keyValidWallets = GlobalKey();
void initState() {
super.initState();
DubpRust.setup();
getAppDirectory();
// _keyWalletsHome.currentState.getAllWalletsNames();
// _keyMyWallets.currentState.getAllWalletsNames();
}
String generatedMnemonic;
bool walletIsGenerated = false;
NewWallet actualWallet;
String newWalletName;
bool hasError = false;
String validPin = 'NO PIN';
String currentText = "";
var pinColor = Colors.grey[300];
Directory appPath;
@override
Widget build(BuildContext context) {
// getAppDirectory();
return Scaffold(
floatingActionButton: Visibility(
visible: (checkIfWalletExist(
'MonWallet')), //!checkIfWalletExist('MonWallet') &&
child: Container(
height: 80.0,
width: 80.0,
child: FittedBox(
child: FloatingActionButton(
heroTag: "buttonGenerateWallet",
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return GenerateWalletsScreen();
}),
).then((value) => setState(() {
this.newWalletName = value;
checkIfWalletExist(value);
}));
},
child: Container(
height: 40.0,
width: 40.0,
child: Icon(Icons.person_add_alt_1_rounded)),
backgroundColor: Color(0xffEFEFBF))))),
body: SafeArea(
child: Column(children: <Widget>[
Visibility(
visible: (!checkIfWalletExist('MonWallet') && !walletIsGenerated),
child: Column(children: <Widget>[
SizedBox(height: 120),
Center(
child: Text("Vous n'avez encore généré aucun portefeuille.",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.w500),
textAlign: TextAlign.center)),
SizedBox(height: 80),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return GenerateWalletsScreen();
}),
).then((value) => setState(() {
this.newWalletName = value;
checkIfWalletExist(value);
})),
child: Text('Générer un portefeuille',
style: TextStyle(fontSize: 20))),
SizedBox(height: 15),
Center(
child: Text("ou",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.w500),
textAlign: TextAlign.center)),
SizedBox(height: 15),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Color(0xffFFD68E), // background
onPrimary: Colors.black, // foreground
),
onPressed: () => importWallet(),
child: Text('Importer un portefeuille existant',
style: TextStyle(fontSize: 20))),
])),
Visibility(
visible: checkIfWalletExist('MonWallet'),
child: MyWalletsScreen(keyMyWallets: _keyWalletsHome))
])));
}
// Future resetWalletState() async {
// final bool _isExist = await checkIfWalletExist('MonWallet');
// print('The wallet exist in resetWalletState(): ' + _isExist.toString());
// // initState();
// // _keyMyWallets.currentState.setState(() {});
// // _keyMyWallets.currentState.initAppDirectory();
// setState(() {
// // getAllWalletsNames();
// // this.walletIsGenerated = true;
// });
// }
bool checkIfWalletExist(_name) {
print('Nom du wallet: ' + _name);
if (this.appPath == null) {
return false;
}
final bool isExist =
File('${this.appPath.path}/wallets/$_name/wallet.dewif').existsSync();
print(this.appPath.path);
print('Wallet existe ? : ' + isExist.toString());
print('Is wallet generated ? : ' + walletIsGenerated.toString());
if (isExist) {
print('Un wallet existe !');
return true;
} else {
return false;
}
}
Future getAppDirectory() async {
this.appPath = await getApplicationDocumentsDirectory();
setState(() {});
}
Future importWallet() async {}
}

View File

@ -21,42 +21,49 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0-nullsafety.3"
version: "2.5.0-nullsafety.1"
barcode:
dependency: transitive
description:
name: barcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.17.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0-nullsafety.1"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.5"
version: "1.1.0-nullsafety.3"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.3"
version: "1.2.0-nullsafety.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.3"
version: "1.1.0-nullsafety.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0-nullsafety.5"
version: "1.15.0-nullsafety.3"
connectivity:
dependency: transitive
description:
@ -112,7 +119,7 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.3"
version: "1.2.0-nullsafety.1"
ffi:
dependency: transitive
description:
@ -288,21 +295,21 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3-nullsafety.3"
version: "0.6.2"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety.3"
version: "0.12.10-nullsafety.1"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.6"
version: "1.3.0-nullsafety.3"
nested:
dependency: transitive
description:
@ -318,7 +325,7 @@ packages:
source: hosted
version: "0.4.5"
package_info:
dependency: transitive
dependency: "direct main"
description:
name: package_info
url: "https://pub.dartlang.org"
@ -330,7 +337,14 @@ packages:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.3"
version: "1.8.0-nullsafety.1"
path_parsing:
dependency: transitive
description:
name: path_parsing
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
path_provider:
dependency: "direct main"
description:
@ -366,6 +380,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+3"
pdf:
dependency: transitive
description:
name: pdf
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
pedantic:
dependency: transitive
description:
@ -415,6 +436,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
printing:
dependency: "direct main"
description:
name: printing
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
process:
dependency: transitive
description:
@ -429,6 +457,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.2+3"
qr:
dependency: transitive
description:
name: qr
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
qrscan:
dependency: "direct main"
description:
@ -457,6 +492,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.12+4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+4"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+11"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.2+7"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+2"
sky_engine:
dependency: transitive
description: flutter
@ -468,28 +545,28 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.4"
version: "1.8.0-nullsafety.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.6"
version: "1.10.0-nullsafety.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
version: "2.1.0-nullsafety.1"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.3"
version: "1.1.0-nullsafety.1"
super_tooltip:
dependency: "direct main"
description:
@ -503,14 +580,14 @@ packages:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.3"
version: "1.2.0-nullsafety.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19-nullsafety.6"
version: "0.2.19-nullsafety.2"
truncate:
dependency: "direct main"
description:
@ -524,7 +601,7 @@ packages:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.5"
version: "1.3.0-nullsafety.3"
uuid:
dependency: transitive
description:
@ -545,7 +622,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.5"
version: "2.1.0-nullsafety.3"
websocket:
dependency: transitive
description:
@ -582,5 +659,5 @@ packages:
source: hosted
version: "2.2.1"
sdks:
dart: ">=2.12.0-0.0 <3.0.0"
flutter: ">=1.22.0"
dart: ">=2.10.0-110 <2.11.0"
flutter: ">=1.22.0 <2.0.0"

View File

@ -5,7 +5,7 @@ description: A new Flutter project.
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.0+9
version: 0.0.0+15
environment:
sdk: ">=2.7.0 <3.0.0"
@ -28,12 +28,15 @@ dependencies:
pin_code_fields: ^6.0.2
http: ^0.12.2
super_tooltip: ^0.9.6
package_info: ^0.4.3+2
printing: ^4.0.0
shared_preferences: ^0.5.12+4
flutter_icons:
android: "ic_launcher"
ios: true
image_path: "assets/icon/gecko5bduniter2.png"
image_path: "assets/icon/gecko_final.png"
cupertino_icons: ^1.0.0
dev_dependencies:
@ -46,4 +49,7 @@ flutter:
assets:
- images/
- config/
- config/
- assets/icon/gecko_final.png
- assets/
- assets/OpenSans-Regular.ttf

View File

@ -12,8 +12,12 @@ echo "Nom du build final: ${APPNAME}-${VERSION}+${BUILD}.apk"
#flutter build apk --split-per-abi --build-name $VERSION --build-number $BUILD
flutter clean
flutter build apk --split-per-abi --target-platform android-arm,android-arm64 --build-name $VERSION --build-number $BUILD
if [[ $1 == "bundle" ]]; then
flutter build appbundle --release --target-platform android-arm,android-arm64 --build-name $VERSION --build-number $BUILD
else
# flutter build apk --release --split-per-abi --target-platform android-arm,android-arm64 --build-name $VERSION --build-number $BUILD
flutter build apk --release --build-name $VERSION --build-number $BUILD
fi
if [[ -d $HOME/Téléchargements ]]; then
DL="$HOME/Téléchargements"

5
scripts/generateIcon.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
flutter pub run flutter_launcher_icons:main
exit 0