From 8ca0b00a60e30959d3cb97085b5b003d7b423fad Mon Sep 17 00:00:00 2001 From: poka Date: Mon, 1 Jan 2024 21:54:28 +0100 Subject: [PATCH] feat: add datapod provider and mutation for avatar --- lib/main.dart | 4 +- lib/models/queries_datapod.dart | 26 +++++++ lib/providers/cesium_plus.dart | 2 +- lib/providers/substrate_sdk.dart | 30 +++---- lib/providers/v2s_datapod.dart | 125 ++++++++++++++++++++++++++++++ lib/providers/wallet_options.dart | 5 ++ lib/screens/search.dart | 1 - 7 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 lib/models/queries_datapod.dart create mode 100644 lib/providers/v2s_datapod.dart diff --git a/lib/main.dart b/lib/main.dart index bc6edfb..8774ced 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -26,6 +26,7 @@ import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/generate_wallets.dart'; import 'package:gecko/providers/settings_provider.dart'; import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:gecko/providers/v2s_datapod.dart'; import 'package:gecko/providers/wallets_profiles.dart'; import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/my_wallets.dart'; @@ -133,7 +134,8 @@ class Gecko extends StatelessWidget { ChangeNotifierProvider(create: (_) => CesiumPlusProvider()), ChangeNotifierProvider(create: (_) => SubstrateSdk()), ChangeNotifierProvider(create: (_) => DuniterIndexer()), - ChangeNotifierProvider(create: (_) => SettingsProvider()) + ChangeNotifierProvider(create: (_) => SettingsProvider()), + ChangeNotifierProvider(create: (_) => V2sDatapodProvider()) ], child: MaterialApp( localizationsDelegates: context.localizationDelegates, diff --git a/lib/models/queries_datapod.dart b/lib/models/queries_datapod.dart new file mode 100644 index 0000000..30e1ac0 --- /dev/null +++ b/lib/models/queries_datapod.dart @@ -0,0 +1,26 @@ +const String updateProfileQ = r''' +mutation ($address: String!, $hash: String!, $signature: String!, $title: String, $description: String, $avatar: String, $geoloc: GeolocInput, $city: String, $socials: [SocialInput!]) { + updateProfile(address: $address, hash: $hash, signature: $signature, title: $title, description: $description, avatarBase64: $avatar, geoloc: $geoloc, city: $city, socials: $socials) { + message + success + } +} +'''; + +const String deleteProfileQ = r''' +mutation ($address: String!, $hash: String!, $signature: String!) { + deleteProfile(address: $address, hash: $hash, signature: $signature) { + message + success + } +} +'''; + +const String migrateProfileQ = r''' +mutation ($addressOld: String!, $addressNew: String!, $hash: String!, $signature: String!) { + migrateProfile(addressOld: $addressOld, addressNew: $addressNew, hash: $hash, signature: $signature) { + message + success + } +} +'''; diff --git a/lib/providers/cesium_plus.dart b/lib/providers/cesium_plus.dart index 92c6fe8..cc01970 100644 --- a/lib/providers/cesium_plus.dart +++ b/lib/providers/cesium_plus.dart @@ -78,7 +78,7 @@ class CesiumPlusProvider with ChangeNotifier { final hashDocHex = hashDoc.toString().toUpperCase(); // Generate signature of document - final signature = await sub.signCsPlusDocument(hashDocHex, address); + final signature = await sub.signDatapod(hashDocHex, address); // Build final document final Map data = { diff --git a/lib/providers/substrate_sdk.dart b/lib/providers/substrate_sdk.dart index d86f975..b367ee6 100644 --- a/lib/providers/substrate_sdk.dart +++ b/lib/providers/substrate_sdk.dart @@ -136,7 +136,7 @@ class SubstrateSdk with ChangeNotifier { return res?.signature ?? ''; } - Future signCsPlusDocument(String document, String address) async { + Future signDatapod(String document, String address) async { final myWallets = Provider.of(homeContext, listen: false); final messageToSign = Uint8List.fromList(document.codeUnits); @@ -232,7 +232,7 @@ class SubstrateSdk with ChangeNotifier { } // Get onchain storage values - final List balanceGlobalMulti = + final List accountMulti = (await _getStorage('system.account.multi($stringifyAddresses)') as List) .map((dynamic e) => e as Map) .toList(); @@ -253,14 +253,10 @@ class SubstrateSdk with ChangeNotifier { .map((dynamic e) => e as Map?) .toList(); - final List pastReevals = - await _getStorage('universalDividend.pastReevals()'); - int nbr = 0; Map> finalBalancesList = {}; - for (Map balanceGlobal in balanceGlobalMulti) { - final computedBalance = - await _computeBalance(idtyDataList[nbr], pastReevals, balanceGlobal); + for (Map account in accountMulti) { + final computedBalance = await _computeBalance(idtyDataList[nbr], account); finalBalancesList.putIfAbsent(addresses[nbr], () => computedBalance); nbr++; } @@ -279,35 +275,33 @@ class SubstrateSdk with ChangeNotifier { } // Get onchain storage values - final Map balanceGlobal = await _getStorage('system.account("$address")'); + final Map account = await _getStorage('system.account("$address")'); final int? idtyIndex = await _getStorage('identity.identityIndexOf("$address")'); final Map? idtyData = idtyIndex == null ? null : await _getStorage('identity.identities($idtyIndex)'); - final List pastReevals = - await _getStorage('universalDividend.pastReevals()'); - return _computeBalance(idtyData, pastReevals, balanceGlobal); + return _computeBalance(idtyData, account); } Future> _computeBalance( - Map? idtyData, List pastReevals, Map balanceGlobal) async { + Map? idtyData, Map account) async { + final List pastReevals = + await _getStorage('universalDividend.pastReevals()'); // Compute amount of claimable UDs currentUdIndex = await getCurrentUdIndex(); final int unclaimedUds = _computeUnclaimUds( idtyData?['data']?['firstEligibleUd'] ?? 0, pastReevals); // Calculate transferable and potential balance - final int transferableBalance = - (balanceGlobal['data']['free'] + unclaimedUds); + final int transferableBalance = (account['data']['free'] + unclaimedUds); return { 'transferableBalance': round((transferableBalance / balanceRatio) / 100), - 'free': round((balanceGlobal['data']['free'] / balanceRatio) / 100), + 'free': round((account['data']['free'] / balanceRatio) / 100), 'unclaimedUds': round((unclaimedUds / balanceRatio) / 100), - 'reserved': - round((balanceGlobal['data']['reserved'] / balanceRatio) / 100), + 'reserved': round((account['data']['reserved'] / balanceRatio) / 100), }; } diff --git a/lib/providers/v2s_datapod.dart b/lib/providers/v2s_datapod.dart new file mode 100644 index 0000000..eba2950 --- /dev/null +++ b/lib/providers/v2s_datapod.dart @@ -0,0 +1,125 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:crypto/crypto.dart'; +import 'package:flutter/material.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/queries_datapod.dart'; +import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:graphql_flutter/graphql_flutter.dart'; +import 'package:provider/provider.dart'; + +class V2sDatapodProvider with ChangeNotifier { + Future _execQuery( + String query, Map variables) async { + final httpLink = HttpLink( + // 'http://10.0.2.2:8080/v1/graphql', + 'https://gdev-datapod.p2p.legal/v1/graphql', + ); + + final GraphQLClient client = GraphQLClient( + cache: GraphQLCache(), + link: httpLink, + ); + + final QueryOptions options = + QueryOptions(document: gql(query), variables: variables); + + return await client.query(options); + } + + Future updateProfile( + {required String address, + String? title, + String? description, + String? avatar, + String? city, + List>? socials, + Map? geoloc}) async { + final sub = Provider.of(homeContext, listen: false); + + final messageToSign = jsonEncode({ + 'address': address, + 'description': description, + 'avatarBase64': avatar, + 'geoloc': geoloc, + 'title': title, + 'city': city, + 'socials': socials + }); + final hashDocBytes = utf8.encode(messageToSign); + final hashDoc = sha256.convert(hashDocBytes).toString().toUpperCase(); + final signature = await sub.signDatapod(hashDoc, address); + + final variables = { + 'address': address, + 'hash': hashDoc, + 'signature': signature, + 'title': title, + 'description': description, + 'avatar': avatar, + 'city': city, + 'socials': socials, + 'geoloc': geoloc, + }; + final result = await _execQuery(updateProfileQ, variables); + if (result.hasException) { + log.e(result.exception.toString()); + return false; + } + log.d(result.data!['updateProfile']['message']); + return true; + } + + Future deleteProfile({required String address}) async { + final sub = Provider.of(homeContext, listen: false); + + final messageToSign = jsonEncode({'address': address}); + final hashDocBytes = utf8.encode(messageToSign); + final hashDoc = sha256.convert(hashDocBytes).toString().toUpperCase(); + final signature = await sub.signDatapod(hashDoc, address); + + final variables = { + 'address': address, + 'hash': hashDoc, + 'signature': signature + }; + final result = await _execQuery(deleteProfileQ, variables); + if (result.hasException) { + log.e(result.exception.toString()); + return false; + } + log.d(result.data!['deleteProfile']['message']); + return true; + } + + Future migrateProfile( + {required String addressOld, required String addressNew}) async { + final sub = Provider.of(homeContext, listen: false); + + final messageToSign = + jsonEncode({'addressOld': addressOld, 'addressNew': addressNew}); + final hashDocBytes = utf8.encode(messageToSign); + final hashDoc = sha256.convert(hashDocBytes).toString().toUpperCase(); + final signature = await sub.signDatapod(hashDoc, addressOld); + + final variables = { + 'addressOld': addressOld, + 'addressNew': addressNew, + 'hash': hashDoc, + 'signature': signature + }; + final result = await _execQuery(migrateProfileQ, variables); + if (result.hasException) { + log.e(result.exception.toString()); + return false; + } + log.d(result.data!['migrateProfile']['message']); + return true; + } + + Future setAvatar(String address, String avatarPath) async { + final avatarBytes = await File(avatarPath).readAsBytes(); + final avatarString = base64Encode(avatarBytes); + return await updateProfile(address: address, avatar: avatarString); + } +} diff --git a/lib/providers/wallet_options.dart b/lib/providers/wallet_options.dart index 03da0df..487bc18 100644 --- a/lib/providers/wallet_options.dart +++ b/lib/providers/wallet_options.dart @@ -11,6 +11,7 @@ import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:gecko/providers/v2s_datapod.dart'; import 'package:gecko/widgets/commons/common_elements.dart'; import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; import 'package:gecko/screens/transaction_in_progress.dart'; @@ -109,6 +110,9 @@ class WalletOptionsProvider with ChangeNotifier { ], ); + final datapod = + Provider.of(homeContext, listen: false); + final newPath = "${imageDirectory.path}/${pickedFile.name}"; if (croppedFile != null) { @@ -117,6 +121,7 @@ class WalletOptionsProvider with ChangeNotifier { log.w('No image selected.'); return ''; } + datapod.setAvatar(address.text, newPath); return newPath; } else { log.w('No image selected.'); diff --git a/lib/screens/search.dart b/lib/screens/search.dart index 63c7bf5..7b6ebb8 100644 --- a/lib/screens/search.dart +++ b/lib/screens/search.dart @@ -1,7 +1,6 @@ // ignore_for_file: use_build_context_synchronously import 'dart:async'; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart';