feat: get remote avatar from datapod, and sync local avatars
This commit is contained in:
parent
639b376dc1
commit
55eb61fb7d
|
@ -8,7 +8,7 @@ import 'package:hive_flutter/hive_flutter.dart';
|
|||
import 'package:logger/logger.dart';
|
||||
|
||||
// Version of box data
|
||||
const int dataVersion = 7;
|
||||
const int dataVersion = 8;
|
||||
|
||||
late String appVersion;
|
||||
const int pinLength = 5;
|
||||
|
@ -21,13 +21,17 @@ late Box configBox;
|
|||
late Box<G1WalletsList> g1WalletsBox;
|
||||
late Box<G1WalletsList> contactsBox;
|
||||
// late Box keystoreBox;
|
||||
late Directory imageDirectory;
|
||||
late Directory avatarsDirectory;
|
||||
late Directory avatarsCacheDirectory;
|
||||
late bool isTall;
|
||||
|
||||
String cesiumPod = "https://g1.data.le-sou.org";
|
||||
const cesiumPod = "https://g1.data.le-sou.org";
|
||||
// String cesiumPod = "https://g1.data.presles.fr";
|
||||
// String cesiumPod = "https://g1.data.e-is.pro";
|
||||
|
||||
const datapodEndpoint = 'https://gdev-datapod.p2p.legal';
|
||||
// const v2sDatapod = 'http://10.0.2.2:8080';
|
||||
|
||||
// Contexts
|
||||
late BuildContext homeContext;
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:gecko/globals.dart';
|
|||
import 'package:gecko/models/chest_data.dart';
|
||||
import 'package:gecko/models/g1_wallets_list.dart';
|
||||
import 'package:gecko/models/wallet_data.dart';
|
||||
import 'package:gecko/providers/cesium_plus.dart';
|
||||
import 'package:gecko/providers/chest_provider.dart';
|
||||
import 'package:gecko/providers/duniter_indexer.dart';
|
||||
import 'package:gecko/providers/generate_wallets.dart';
|
||||
|
@ -55,7 +54,7 @@ Future<void> main() async {
|
|||
// await dotenv.load();
|
||||
// }
|
||||
|
||||
HomeProvider homeProvider = HomeProvider();
|
||||
final homeProvider = HomeProvider();
|
||||
// DuniterIndexer _duniterIndexer = DuniterIndexer();
|
||||
|
||||
await initHiveForFlutter();
|
||||
|
@ -131,7 +130,6 @@ class Gecko extends StatelessWidget {
|
|||
ChangeNotifierProvider(create: (_) => GenerateWalletsProvider()),
|
||||
ChangeNotifierProvider(create: (_) => WalletOptionsProvider()),
|
||||
ChangeNotifierProvider(create: (_) => SearchProvider()),
|
||||
ChangeNotifierProvider(create: (_) => CesiumPlusProvider()),
|
||||
ChangeNotifierProvider(create: (_) => SubstrateSdk()),
|
||||
ChangeNotifierProvider(create: (_) => DuniterIndexer()),
|
||||
ChangeNotifierProvider(create: (_) => SettingsProvider()),
|
||||
|
|
|
@ -24,3 +24,19 @@ mutation ($addressOld: String!, $addressNew: String!, $hash: String!, $signature
|
|||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String getAvatarQ = r'''
|
||||
query ($address: String!) {
|
||||
profiles_by_pk(address: $address) {
|
||||
avatar64
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
const String profileEditedAtQ = r'''
|
||||
query ($address: String!) {
|
||||
profiles_by_pk(address: $address) {
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:gecko/globals.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
part 'wallet_data.g.dart';
|
||||
|
||||
@HiveType(typeId: 0)
|
||||
|
@ -36,6 +44,9 @@ class WalletData extends HiveObject {
|
|||
@HiveField(10)
|
||||
List<int>? certs;
|
||||
|
||||
@HiveField(11)
|
||||
DateTime? profileUpdatedTime;
|
||||
|
||||
WalletData({
|
||||
required this.address,
|
||||
this.chest,
|
||||
|
@ -44,6 +55,7 @@ class WalletData extends HiveObject {
|
|||
this.derivation,
|
||||
this.imageDefaultPath,
|
||||
this.imageCustomPath,
|
||||
this.profileUpdatedTime,
|
||||
this.isOwned = false,
|
||||
this.identityStatus = IdtyStatus.unknown,
|
||||
this.balance = 0,
|
||||
|
@ -75,11 +87,47 @@ class WalletData extends HiveObject {
|
|||
return balance != 0;
|
||||
}
|
||||
|
||||
Future<DateTime?> getUpdatedTime() async {
|
||||
final datapod = Provider.of<V2sDatapodProvider>(homeContext, listen: false);
|
||||
return await datapod.profileEditedAt(address);
|
||||
}
|
||||
|
||||
Future<bool> shouldUpdateProfile() async {
|
||||
final remoteUpdatedProfile = await getUpdatedTime();
|
||||
late Duration difference;
|
||||
if (profileUpdatedTime != null && remoteUpdatedProfile != null) {
|
||||
difference = profileUpdatedTime!.difference(remoteUpdatedProfile);
|
||||
} else if (remoteUpdatedProfile != null) {
|
||||
return true;
|
||||
} else {
|
||||
difference = Duration.zero;
|
||||
}
|
||||
return difference.inSeconds.abs() >= 30;
|
||||
}
|
||||
|
||||
/// This method get the remote avatar on v2s-datapod only if needed, and store it on disk
|
||||
Future getDatapodAvatar() async {
|
||||
if (!await shouldUpdateProfile()) return;
|
||||
|
||||
final datapod = Provider.of<V2sDatapodProvider>(homeContext, listen: false);
|
||||
final avatarUuid = const Uuid().v4();
|
||||
|
||||
await datapod.getAvatar(address, saveOnDisk: true, uuid: avatarUuid);
|
||||
|
||||
final avatarPath = '${avatarsDirectory.path}/$address-$avatarUuid';
|
||||
if (!await File(avatarPath).exists()) return;
|
||||
|
||||
profileUpdatedTime = await getUpdatedTime();
|
||||
imageCustomPath = avatarPath;
|
||||
|
||||
walletBox.put(address, this);
|
||||
datapod.reload();
|
||||
}
|
||||
|
||||
bool hasCustomImage() {
|
||||
return imageCustomPath != null;
|
||||
}
|
||||
|
||||
// returns only the id part of the ':'-separated string
|
||||
List<int?> id() {
|
||||
return [chest, number];
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class WalletDataAdapter extends TypeAdapter<WalletData> {
|
|||
derivation: fields[4] as int?,
|
||||
imageDefaultPath: fields[5] as String?,
|
||||
imageCustomPath: fields[6] as String?,
|
||||
profileUpdatedTime: fields[11] as DateTime?,
|
||||
isOwned: fields[7] as bool,
|
||||
identityStatus: fields[8] as IdtyStatus,
|
||||
balance: fields[9] as double,
|
||||
|
@ -34,7 +35,7 @@ class WalletDataAdapter extends TypeAdapter<WalletData> {
|
|||
@override
|
||||
void write(BinaryWriter writer, WalletData obj) {
|
||||
writer
|
||||
..writeByte(11)
|
||||
..writeByte(12)
|
||||
..writeByte(0)
|
||||
..write(obj.address)
|
||||
..writeByte(1)
|
||||
|
@ -56,7 +57,9 @@ class WalletDataAdapter extends TypeAdapter<WalletData> {
|
|||
..writeByte(9)
|
||||
..write(obj.balance)
|
||||
..writeByte(10)
|
||||
..write(obj.certs);
|
||||
..write(obj.certs)
|
||||
..writeByte(11)
|
||||
..write(obj.profileUpdatedTime);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,261 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:gecko/globals.dart';
|
||||
import 'package:gecko/models/scale_functions.dart';
|
||||
import 'package:gecko/providers/substrate_sdk.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
// import 'package:http/http.dart' as http;
|
||||
|
||||
class CesiumPlusProvider with ChangeNotifier {
|
||||
final cesiumName = TextEditingController();
|
||||
|
||||
CancelToken avatarCancelToken = CancelToken();
|
||||
|
||||
final Map<String, String> _headers = {
|
||||
'Content-type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
|
||||
// List _buildQueryGetAvatar(String pubkeyV1) {
|
||||
// final queryGetAvatar = json.encode({
|
||||
// "query": {
|
||||
// "bool": {
|
||||
// "should": [
|
||||
// {
|
||||
// "match": {
|
||||
// '_id': {"query": pubkeyV1, "boost": 1}
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "prefix": {'_id': pubkeyV1}
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
// "_source": [
|
||||
// "avatar",
|
||||
// "avatar._content_type",
|
||||
// ],
|
||||
// "indices_boost": {"user": 1, "page": 1, "group": 0.01}
|
||||
// });
|
||||
|
||||
// const requestUrl = "/user,page,group/profile,record/_search";
|
||||
// final podRequest = cesiumPod + requestUrl;
|
||||
|
||||
// return [podRequest, queryGetAvatar];
|
||||
// }
|
||||
|
||||
Future<List> _buildQuerySetAvatar(
|
||||
String pubkeyV1, String address, String avatar) async {
|
||||
int timeSent = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
final queryGetAvatar = json.encode({
|
||||
"avatar": {"_content": avatar, "_content_type": "image/png"},
|
||||
"time": timeSent,
|
||||
"issuer": pubkeyV1,
|
||||
"version": 2,
|
||||
"tags": []
|
||||
});
|
||||
|
||||
final requestUrl =
|
||||
"/user/profile?pubkey=$pubkeyV1/_update?pubkey=$pubkeyV1";
|
||||
final podRequest = cesiumPod + requestUrl;
|
||||
|
||||
final signedDocument = await signDoc(queryGetAvatar, address);
|
||||
|
||||
return [podRequest, signedDocument];
|
||||
}
|
||||
|
||||
Future<String> signDoc(String document, String address) async {
|
||||
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
|
||||
final hashDocBytes = utf8.encode(document);
|
||||
final hashDoc = sha256.convert(hashDocBytes);
|
||||
final hashDocHex = hashDoc.toString().toUpperCase();
|
||||
|
||||
// Generate signature of document
|
||||
final signature = await sub.signDatapod(hashDocHex, address);
|
||||
|
||||
// Build final document
|
||||
final Map<String, dynamic> data = {
|
||||
'hash': hashDocHex,
|
||||
'signature': signature
|
||||
};
|
||||
final signJSON = jsonEncode(data);
|
||||
final Map<String, dynamic> finalJSON = {
|
||||
...jsonDecode(signJSON),
|
||||
...jsonDecode(document)
|
||||
};
|
||||
|
||||
return jsonEncode(finalJSON);
|
||||
}
|
||||
// Future<String> getName(String address) async {
|
||||
// String? name;
|
||||
|
||||
// if (g1WalletsBox.get(address)?.csName != null) {
|
||||
// return g1WalletsBox.get(address)!.csName!;
|
||||
// }
|
||||
|
||||
// List queryOptions = await _buildQueryName(address);
|
||||
|
||||
// var dio = Dio();
|
||||
// late Response response;
|
||||
// try {
|
||||
// response = await dio.post(
|
||||
// queryOptions[0],
|
||||
// data: queryOptions[1],
|
||||
// options: Options(
|
||||
// headers: queryOptions[2],
|
||||
// sendTimeout: const Duration(seconds: 3),
|
||||
// receiveTimeout: const Duration(seconds: 5),
|
||||
// ),
|
||||
// );
|
||||
// // response = await http.post((Uri.parse(queryOptions[0])),
|
||||
// // body: queryOptions[1], headers: queryOptions[2]);
|
||||
// } catch (e) {
|
||||
// log.e(e);
|
||||
// }
|
||||
|
||||
// if (response.data['hits']['hits'].toString() == '[]') {
|
||||
// return '';
|
||||
// }
|
||||
// final bool nameExist =
|
||||
// response.data['hits']['hits'][0]['_source'].containsKey("title");
|
||||
// if (!nameExist) {
|
||||
// return '';
|
||||
// }
|
||||
// name = response.data['hits']['hits'][0]['_source']['title'];
|
||||
|
||||
// name ??= '';
|
||||
// g1WalletsBox.get(address)!.csName = name;
|
||||
|
||||
// return name;
|
||||
// }
|
||||
|
||||
Future<Image> getAvatar(String address, double size) async {
|
||||
return defaultAvatar(size);
|
||||
// final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
|
||||
// if (await isAvatarExist(address)) {
|
||||
// return await getAvatarLocal(address, size);
|
||||
// }
|
||||
|
||||
// final pubkeyV1 = await sub.addressToPubkeyB58(address);
|
||||
// var dio = Dio();
|
||||
|
||||
// List queryOptions = _buildQueryGetAvatar(pubkeyV1);
|
||||
|
||||
// late Response response;
|
||||
// try {
|
||||
// response = await dio
|
||||
// .post(queryOptions[0],
|
||||
// data: queryOptions[1],
|
||||
// options: Options(
|
||||
// headers: _headers,
|
||||
// sendTimeout: const Duration(seconds: 4),
|
||||
// receiveTimeout: const Duration(seconds: 15),
|
||||
// ),
|
||||
// cancelToken: avatarCancelToken)
|
||||
// .timeout(
|
||||
// const Duration(seconds: 15),
|
||||
// );
|
||||
// } catch (e) {
|
||||
// log.e(e);
|
||||
// }
|
||||
|
||||
// if (response.data['hits']['hits'].toString() == '[]' ||
|
||||
// !response.data['hits']['hits'][0]['_source'].containsKey("avatar")) {
|
||||
// return defaultAvatar(size);
|
||||
// }
|
||||
|
||||
// final avatar =
|
||||
// response.data['hits']['hits'][0]['_source']['avatar']['_content'];
|
||||
|
||||
// final avatarFile = await saveAvatar(address, avatar);
|
||||
|
||||
// final finalAvatar = Image.file(
|
||||
// avatarFile,
|
||||
// height: size,
|
||||
// fit: BoxFit.fitWidth,
|
||||
// );
|
||||
|
||||
// return finalAvatar;
|
||||
}
|
||||
|
||||
Future<bool> setAvatar(String address, String avatarPath) async {
|
||||
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
|
||||
|
||||
final pubkeyV1 = await sub.addressToPubkeyB58(address);
|
||||
var dio = Dio();
|
||||
final Uint8List avatarBytes = await File(avatarPath).readAsBytes();
|
||||
final avatarString = base64Encode(avatarBytes);
|
||||
|
||||
List queryOptions =
|
||||
await _buildQuerySetAvatar(pubkeyV1, address, avatarString);
|
||||
|
||||
late Response response;
|
||||
try {
|
||||
response = await dio
|
||||
.post(queryOptions[0],
|
||||
data: queryOptions[1],
|
||||
options: Options(
|
||||
headers: _headers,
|
||||
sendTimeout: const Duration(seconds: 4),
|
||||
receiveTimeout: const Duration(seconds: 15),
|
||||
),
|
||||
cancelToken: avatarCancelToken)
|
||||
.timeout(
|
||||
const Duration(seconds: 15),
|
||||
);
|
||||
return response.statusCode == 200;
|
||||
} catch (e) {
|
||||
log.e(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> getLocalPath() async {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
return directory.path;
|
||||
}
|
||||
|
||||
Future<File> saveAvatar(String address, String data) async {
|
||||
final path = await getLocalPath();
|
||||
final avatarFolder = Directory('$path/avatars/');
|
||||
if (!await avatarFolder.exists()) {
|
||||
await avatarFolder.create();
|
||||
}
|
||||
final file = File('$path/avatars/$address');
|
||||
return await file.writeAsBytes(base64.decode(data));
|
||||
}
|
||||
|
||||
Future<Image> getAvatarLocal(String address, double size) async {
|
||||
final path = await getLocalPath();
|
||||
final avatarFile = File('$path/avatars/$address');
|
||||
return Image.file(
|
||||
avatarFile,
|
||||
height: size,
|
||||
fit: BoxFit.fitWidth,
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> isAvatarExist(String address) async {
|
||||
final path = await getLocalPath();
|
||||
final avatarFile = File('$path/avatars/$address');
|
||||
return avatarFile.exists();
|
||||
}
|
||||
|
||||
Future deleteAvatarFolder() async {
|
||||
final path = await getLocalPath();
|
||||
final avatarFolder = Directory('$path/avatars/');
|
||||
if (await avatarFolder.exists()) {
|
||||
await avatarFolder.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image defaultAvatar(double size) =>
|
||||
Image.asset(('assets/icon_user.png'), height: scaleSize(size));
|
|
@ -48,10 +48,11 @@ class HomeProvider with ChangeNotifier {
|
|||
|
||||
// Init app folders
|
||||
final documentDir = await getApplicationDocumentsDirectory();
|
||||
imageDirectory = Directory('${documentDir.path}/images');
|
||||
avatarsDirectory = Directory('${documentDir.path}/avatars');
|
||||
avatarsCacheDirectory = Directory('${documentDir.path}/avatarsCache');
|
||||
|
||||
if (!await imageDirectory.exists()) {
|
||||
await imageDirectory.create();
|
||||
if (!await avatarsDirectory.exists()) {
|
||||
await avatarsDirectory.create();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
@ -5,6 +7,7 @@ import 'package:gecko/globals.dart';
|
|||
import 'package:gecko/models/wallet_data.dart';
|
||||
import 'package:gecko/providers/substrate_sdk.dart';
|
||||
import 'package:gecko/widgets/commons/common_elements.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MyWalletsProvider with ChangeNotifier {
|
||||
|
@ -112,6 +115,12 @@ class MyWalletsProvider with ChangeNotifier {
|
|||
await configBox.delete('defaultWallet');
|
||||
await sub.deleteAllAccounts();
|
||||
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final avatarFolder = Directory('${directory.path}/avatars/');
|
||||
if (await avatarFolder.exists()) {
|
||||
await avatarFolder.delete(recursive: true);
|
||||
}
|
||||
|
||||
myWalletProvider.pinCode = '';
|
||||
|
||||
await Navigator.of(context)
|
||||
|
|
|
@ -4,17 +4,17 @@ 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/models/scale_functions.dart';
|
||||
import 'package:gecko/providers/my_wallets.dart';
|
||||
import 'package:gecko/providers/substrate_sdk.dart';
|
||||
import 'package:graphql_flutter/graphql_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class V2sDatapodProvider with ChangeNotifier {
|
||||
Future<QueryResult> _execQuery(
|
||||
String query, Map<String, dynamic> variables) async {
|
||||
final httpLink = HttpLink(
|
||||
// 'http://10.0.2.2:8080/v1/graphql',
|
||||
'https://gdev-datapod.p2p.legal/v1/graphql',
|
||||
);
|
||||
final httpLink = HttpLink('$datapodEndpoint/v1/graphql');
|
||||
|
||||
final GraphQLClient client = GraphQLClient(
|
||||
cache: GraphQLCache(),
|
||||
|
@ -36,6 +36,9 @@ class V2sDatapodProvider with ChangeNotifier {
|
|||
List<Map<String, String>>? socials,
|
||||
Map<String, double>? geoloc}) async {
|
||||
final sub = Provider.of<SubstrateSdk>(homeContext, listen: false);
|
||||
final myWallets =
|
||||
Provider.of<MyWalletsProvider>(homeContext, listen: false);
|
||||
final walletData = myWallets.getWalletDataByAddress(address);
|
||||
|
||||
final messageToSign = jsonEncode({
|
||||
'address': address,
|
||||
|
@ -67,6 +70,9 @@ class V2sDatapodProvider with ChangeNotifier {
|
|||
return false;
|
||||
}
|
||||
log.d(result.data!['updateProfile']['message']);
|
||||
walletData!.profileUpdatedTime = DateTime.now();
|
||||
walletBox.put(address, walletData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -122,4 +128,93 @@ class V2sDatapodProvider with ChangeNotifier {
|
|||
final avatarString = base64Encode(avatarBytes);
|
||||
return await updateProfile(address: address, avatar: avatarString);
|
||||
}
|
||||
|
||||
Future<DateTime?> profileEditedAt(String address) async {
|
||||
final variables = <String, dynamic>{
|
||||
'address': address,
|
||||
};
|
||||
final result = await _execQuery(profileEditedAtQ, variables);
|
||||
if (result.hasException) {
|
||||
log.e(result.exception.toString());
|
||||
return null;
|
||||
}
|
||||
final String? profileDateData =
|
||||
result.data!['profiles_by_pk']?['updated_at'];
|
||||
final profileDate =
|
||||
profileDateData == null ? null : DateTime.tryParse(profileDateData);
|
||||
return profileDate;
|
||||
}
|
||||
|
||||
Future<Image> getAvatar(String address,
|
||||
{double size = 20, bool saveOnDisk = false, String? uuid}) async {
|
||||
final variables = <String, dynamic>{
|
||||
'address': address,
|
||||
};
|
||||
final result = await _execQuery(getAvatarQ, variables);
|
||||
if (result.hasException) {
|
||||
log.e(result.exception.toString());
|
||||
return defaultAvatar(size);
|
||||
}
|
||||
final String? avatar64 = result.data!['profiles_by_pk']?['avatar64'];
|
||||
|
||||
if (avatar64 == null) {
|
||||
return defaultAvatar(size);
|
||||
}
|
||||
|
||||
final sanitizedAvatar64 =
|
||||
avatar64.replaceAll('\n', '').replaceAll('\r', '').replaceAll(' ', '');
|
||||
|
||||
if (saveOnDisk) {
|
||||
log.d('We save avatar for $address');
|
||||
await saveAvatar(address, sanitizedAvatar64, uuid);
|
||||
} else {
|
||||
await cacheAvatar(address, sanitizedAvatar64);
|
||||
}
|
||||
|
||||
return Image.memory(
|
||||
base64.decode(sanitizedAvatar64),
|
||||
height: size,
|
||||
fit: BoxFit.fitWidth,
|
||||
);
|
||||
}
|
||||
|
||||
Future<File> saveAvatar(String address, String data, String? uuid) async {
|
||||
uuid = uuid ?? const Uuid().v4();
|
||||
final file = File('${avatarsDirectory.path}/$address-$uuid');
|
||||
return await file.writeAsBytes(base64.decode(data));
|
||||
}
|
||||
|
||||
Future<File> cacheAvatar(String address, String data) async {
|
||||
final file = File('${avatarsCacheDirectory.path}/$address');
|
||||
return await file.writeAsBytes(base64.decode(data));
|
||||
}
|
||||
|
||||
Image getAvatarLocal(String address, double size) {
|
||||
final avatarFile = File('${avatarsCacheDirectory.path}/$address');
|
||||
return Image.file(
|
||||
avatarFile,
|
||||
height: size,
|
||||
fit: BoxFit.fitWidth,
|
||||
);
|
||||
}
|
||||
|
||||
Image defaultAvatar(double size) =>
|
||||
Image.asset(('assets/icon_user.png'), height: scaleSize(size));
|
||||
|
||||
Future deleteAvatarsCacheDirectory() async {
|
||||
if (await avatarsCacheDirectory.exists()) {
|
||||
await avatarsCacheDirectory.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future deleteAvatarsDirectory() async {
|
||||
if (await avatarsDirectory.exists()) {
|
||||
await avatarsDirectory.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reload() {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,13 +49,13 @@ class WalletOptionsProvider with ChangeNotifier {
|
|||
|
||||
Future<int> deleteWallet(context, WalletData wallet) async {
|
||||
final sub = Provider.of<SubstrateSdk>(context, listen: false);
|
||||
final datapod = Provider.of<V2sDatapodProvider>(context, listen: false);
|
||||
final bool? answer = await (confirmPopup(
|
||||
context, 'areYouSureToForgetWallet'.tr(args: [wallet.name!])));
|
||||
|
||||
if (answer ?? false) {
|
||||
//Check if balance is null
|
||||
final balance = await sub.getBalance(wallet.address);
|
||||
if (balance != {}) {
|
||||
if (balanceCache[wallet.address] != 0) {
|
||||
final myWalletProvider =
|
||||
Provider.of<MyWalletsProvider>(context, listen: false);
|
||||
final defaultWallet = myWalletProvider.getDefaultWallet();
|
||||
|
@ -67,6 +67,11 @@ class WalletOptionsProvider with ChangeNotifier {
|
|||
}
|
||||
|
||||
await walletBox.delete(wallet.key);
|
||||
if (wallet.imageCustomPath != null) {
|
||||
final avatarFile = File(wallet.imageCustomPath!);
|
||||
await avatarFile.delete();
|
||||
}
|
||||
datapod.deleteProfile(address: wallet.address);
|
||||
await sub.deleteAccounts([wallet.address]);
|
||||
|
||||
Navigator.pop(context);
|
||||
|
@ -88,7 +93,7 @@ class WalletOptionsProvider with ChangeNotifier {
|
|||
|
||||
if (pickedFile != null) {
|
||||
File imageFile = File(pickedFile.path);
|
||||
if (!await imageDirectory.exists()) {
|
||||
if (!await avatarsDirectory.exists()) {
|
||||
log.e("Image folder doesn't exist");
|
||||
return '';
|
||||
}
|
||||
|
@ -111,7 +116,7 @@ class WalletOptionsProvider with ChangeNotifier {
|
|||
],
|
||||
);
|
||||
|
||||
final newPath = "${imageDirectory.path}/${pickedFile.name}";
|
||||
final newPath = "${avatarsDirectory.path}/${address.text}";
|
||||
|
||||
if (croppedFile != null) {
|
||||
await File(croppedFile.path).rename(newPath);
|
||||
|
@ -140,7 +145,7 @@ class WalletOptionsProvider with ChangeNotifier {
|
|||
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
barrierDismissible: true, // user must tap button!
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:gecko/models/chest_data.dart';
|
|||
import 'package:gecko/models/g1_wallets_list.dart';
|
||||
import 'package:gecko/models/scale_functions.dart';
|
||||
import 'package:gecko/models/widgets_keys.dart';
|
||||
import 'package:gecko/providers/cesium_plus.dart';
|
||||
import 'package:gecko/providers/chest_provider.dart';
|
||||
import 'package:gecko/providers/duniter_indexer.dart';
|
||||
import 'package:gecko/providers/home.dart';
|
||||
|
@ -13,6 +12,7 @@ import 'package:gecko/providers/substrate_sdk.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:gecko/providers/my_wallets.dart';
|
||||
import 'package:gecko/models/wallet_data.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:gecko/widgets/bubble_speak.dart';
|
||||
import 'package:gecko/widgets/commons/animated_text.dart';
|
||||
import 'package:gecko/widgets/commons/common_elements.dart';
|
||||
|
@ -40,8 +40,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
Provider.of<DuniterIndexer>(context, listen: false);
|
||||
final myWalletProvider =
|
||||
Provider.of<MyWalletsProvider>(context, listen: false);
|
||||
final csProvider =
|
||||
Provider.of<CesiumPlusProvider>(context, listen: false);
|
||||
final datapod = Provider.of<V2sDatapodProvider>(context, listen: false);
|
||||
|
||||
final bool isWalletsExists = myWalletProvider.isWalletsExists();
|
||||
|
||||
|
@ -54,6 +53,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
await infoPopup(context, "chestNotCompatibleMustReinstallGecko".tr());
|
||||
await Hive.deleteBoxFromDisk('walletBox');
|
||||
await Hive.deleteBoxFromDisk('chestBox');
|
||||
await datapod.deleteAvatarsDirectory();
|
||||
await avatarsDirectory.create();
|
||||
chestBox = await Hive.openBox<ChestData>("chestBox");
|
||||
await configBox.delete('defaultWallet');
|
||||
if (!sub.sdkReady && !sub.sdkLoading) await sub.initApi();
|
||||
|
@ -67,7 +68,8 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||
if (sub.sdkReady && !sub.nodeConnected) {
|
||||
walletBox = await Hive.openBox<WalletData>("walletBox");
|
||||
await Hive.deleteBoxFromDisk('g1WalletsBox');
|
||||
await csProvider.deleteAvatarFolder();
|
||||
await datapod.deleteAvatarsCacheDirectory();
|
||||
await avatarsCacheDirectory.create();
|
||||
g1WalletsBox = await Hive.openBox<G1WalletsList>("g1WalletsBox");
|
||||
contactsBox = await Hive.openBox<G1WalletsList>("contactsBox");
|
||||
|
||||
|
|
|
@ -10,6 +10,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/providers/wallet_options.dart';
|
||||
import 'package:gecko/providers/wallets_profiles.dart';
|
||||
import 'package:gecko/screens/certifications.dart';
|
||||
|
@ -258,55 +259,58 @@ class WalletOptions extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget avatar(WalletOptionsProvider walletProvider) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
final newPath = await (walletProvider.changeAvatar());
|
||||
if (newPath != '') {
|
||||
wallet.imageCustomPath = newPath;
|
||||
walletBox.put(wallet.key, wallet);
|
||||
// Uncomment to enable Cs+ avatar storage
|
||||
// CesiumPlusProvider().setAvatar(wallet.address, newPath);
|
||||
}
|
||||
walletProvider.reload();
|
||||
},
|
||||
child: wallet.imageCustomPath == null || wallet.imageCustomPath == ''
|
||||
? Image.asset(
|
||||
'assets/avatars/${wallet.imageDefaultPath}',
|
||||
width: scaleSize(122),
|
||||
)
|
||||
: Container(
|
||||
width: scaleSize(122),
|
||||
height: scaleSize(122),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: FileImage(
|
||||
File(wallet.imageCustomPath!),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: InkWell(
|
||||
return Consumer<V2sDatapodProvider>(builder: (context, datapod, _) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
wallet.imageCustomPath = await (walletProvider.changeAvatar());
|
||||
final newPath = await (walletProvider.changeAvatar());
|
||||
if (newPath != '') {
|
||||
wallet.imageCustomPath = newPath;
|
||||
walletBox.put(wallet.key, wallet);
|
||||
// Uncomment to enable Cs+ avatar storage
|
||||
// CesiumPlusProvider().setAvatar(wallet.address, newPath);
|
||||
}
|
||||
walletProvider.reload();
|
||||
},
|
||||
child: Image.asset(
|
||||
'assets/walletOptions/camera.png',
|
||||
height: scaleSize(38),
|
||||
child:
|
||||
wallet.imageCustomPath == null || wallet.imageCustomPath == ''
|
||||
? Image.asset(
|
||||
'assets/avatars/${wallet.imageDefaultPath}',
|
||||
width: scaleSize(122),
|
||||
)
|
||||
: Container(
|
||||
width: scaleSize(122),
|
||||
height: scaleSize(122),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: FileImage(
|
||||
File(wallet.imageCustomPath!),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
wallet.imageCustomPath = await (walletProvider.changeAvatar());
|
||||
walletProvider.reload();
|
||||
},
|
||||
child: Image.asset(
|
||||
'assets/walletOptions/camera.png',
|
||||
height: scaleSize(38),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget confirmIdentityButton(WalletOptionsProvider walletProvider) {
|
||||
|
|
|
@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:gecko/globals.dart';
|
||||
import 'package:gecko/models/scale_functions.dart';
|
||||
import 'package:gecko/models/widgets_keys.dart';
|
||||
import 'package:gecko/providers/cesium_plus.dart';
|
||||
import 'package:gecko/providers/substrate_sdk.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:gecko/screens/wallet_view.dart';
|
||||
|
||||
class CertTile extends StatelessWidget {
|
||||
|
@ -29,7 +29,7 @@ class CertTile extends StatelessWidget {
|
|||
contentPadding: EdgeInsets.only(
|
||||
left: 10, right: 0, top: scaleSize(3), bottom: scaleSize(3)),
|
||||
leading: ClipOval(
|
||||
child: defaultAvatar(avatarSize),
|
||||
child: V2sDatapodProvider().defaultAvatar(avatarSize),
|
||||
),
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:gecko/globals.dart';
|
||||
import 'package:gecko/providers/cesium_plus.dart';
|
||||
import 'package:gecko/widgets/commons/loading.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CesiumAvatar extends StatelessWidget {
|
||||
const CesiumAvatar({Key? key, required this.address, this.size = 15})
|
||||
: super(key: key);
|
||||
final String address;
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final csProvider = Provider.of<CesiumPlusProvider>(context, listen: false);
|
||||
|
||||
return ClipOval(
|
||||
child: FutureBuilder(
|
||||
future: csProvider.getAvatar(address, size),
|
||||
builder: ((context, AsyncSnapshot<Image> avatar) {
|
||||
if (avatar.hasError) {
|
||||
log.e(avatar.error);
|
||||
return (Icon(Icons.close_outlined,
|
||||
color: Colors.red, size: size));
|
||||
} else if (avatar.connectionState != ConnectionState.done) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: const FractionallySizedBox(
|
||||
widthFactor: 0.6, heightFactor: 0.6, child: Loading()));
|
||||
}
|
||||
return avatar.data!;
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -142,14 +142,14 @@ Future<bool?> confirmPopupCertification(BuildContext context, String question1,
|
|||
Future<void> infoPopup(BuildContext context, String title) async {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true, // user must tap button!
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: backgroundColor,
|
||||
content: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: scaledTextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
||||
style: scaledTextStyle(fontSize: 17, fontWeight: FontWeight.w500),
|
||||
),
|
||||
actions: <Widget>[
|
||||
Row(
|
||||
|
@ -162,7 +162,7 @@ Future<void> infoPopup(BuildContext context, String title) async {
|
|||
child: Text(
|
||||
"gotit".tr(),
|
||||
style: scaledTextStyle(
|
||||
fontSize: 21,
|
||||
fontSize: 19,
|
||||
color: const Color(0xffD80000),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'package:gecko/providers/substrate_sdk.dart';
|
|||
import 'package:gecko/providers/wallets_profiles.dart';
|
||||
import 'package:gecko/screens/wallet_view.dart';
|
||||
import 'package:gecko/widgets/balance.dart';
|
||||
import 'package:gecko/widgets/cesium_avatar.dart';
|
||||
import 'package:gecko/widgets/datapod_avatar.dart';
|
||||
import 'package:gecko/widgets/name_by_address.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -47,7 +47,7 @@ class ContactsList extends StatelessWidget {
|
|||
horizontalTitleGap: 7,
|
||||
contentPadding: const EdgeInsets.all(5),
|
||||
dense: !isTall,
|
||||
leading: CesiumAvatar(
|
||||
leading: DatapodAvatar(
|
||||
address: g1Wallet.address, size: scaleSize(50)),
|
||||
title: Row(children: <Widget>[
|
||||
Text(getShortPubkey(g1Wallet.address),
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gecko/globals.dart';
|
||||
import 'package:gecko/models/queries_datapod.dart';
|
||||
import 'package:gecko/models/scale_functions.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:gecko/widgets/commons/loading.dart';
|
||||
import 'package:graphql_flutter/graphql_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class DatapodAvatar extends StatelessWidget {
|
||||
const DatapodAvatar({Key? key, required this.address, this.size = 15})
|
||||
: super(key: key);
|
||||
final String address;
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final datapod = Provider.of<V2sDatapodProvider>(context, listen: false);
|
||||
|
||||
final cachedImage = File('${avatarsCacheDirectory.path}/$address');
|
||||
if (cachedImage.existsSync()) {
|
||||
return ScaledSizedBox(
|
||||
width: size,
|
||||
child: ClipOval(
|
||||
child: datapod.getAvatarLocal(address, size),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final httpLink = HttpLink(
|
||||
'$datapodEndpoint/v1/graphql',
|
||||
);
|
||||
|
||||
final client = ValueNotifier(
|
||||
GraphQLClient(
|
||||
cache: GraphQLCache(store: HiveStore()),
|
||||
link: httpLink,
|
||||
),
|
||||
);
|
||||
|
||||
return ScaledSizedBox(
|
||||
width: size,
|
||||
child: GraphQLProvider(
|
||||
client: client,
|
||||
child: Query(
|
||||
options: QueryOptions(
|
||||
document: gql(getAvatarQ),
|
||||
variables: <String, dynamic>{
|
||||
'address': address,
|
||||
},
|
||||
),
|
||||
builder: (QueryResult result, {fetchMore, refetch}) {
|
||||
if (result.isLoading) {
|
||||
return const Center(
|
||||
child: Loading(),
|
||||
);
|
||||
}
|
||||
final String? avatar64 =
|
||||
result.data!['profiles_by_pk']?['avatar64'];
|
||||
|
||||
if (avatar64 == null || result.data == null) {
|
||||
return ClipOval(child: datapod.defaultAvatar(size));
|
||||
}
|
||||
|
||||
final sanitizedAvatar64 = avatar64
|
||||
.replaceAll('\n', '')
|
||||
.replaceAll('\r', '')
|
||||
.replaceAll(' ', '');
|
||||
|
||||
datapod.cacheAvatar(address, sanitizedAvatar64);
|
||||
|
||||
return ClipOval(
|
||||
child: Image.memory(
|
||||
base64.decode(sanitizedAvatar64),
|
||||
height: size,
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import 'package:gecko/providers/wallets_profiles.dart';
|
|||
import 'package:gecko/screens/certifications.dart';
|
||||
import 'package:gecko/widgets/balance.dart';
|
||||
import 'package:gecko/widgets/certifications.dart';
|
||||
import 'package:gecko/widgets/cesium_avatar.dart';
|
||||
import 'package:gecko/widgets/datapod_avatar.dart';
|
||||
import 'package:gecko/widgets/commons/offline_info.dart';
|
||||
import 'package:gecko/widgets/idty_status.dart';
|
||||
import 'package:gecko/widgets/page_route_no_transition.dart';
|
||||
|
@ -111,7 +111,7 @@ class HeaderProfile extends StatelessWidget {
|
|||
// ScaledSizedBox(width: 20),
|
||||
Column(children: <Widget>[
|
||||
ScaledSizedBox(height: 15),
|
||||
CesiumAvatar(address: address, size: avatarSize),
|
||||
DatapodAvatar(address: address, size: avatarSize),
|
||||
]),
|
||||
]),
|
||||
),
|
||||
|
|
|
@ -11,7 +11,7 @@ import 'package:gecko/providers/substrate_sdk.dart';
|
|||
import 'package:gecko/providers/wallets_profiles.dart';
|
||||
import 'package:gecko/screens/wallet_view.dart';
|
||||
import 'package:gecko/widgets/balance.dart';
|
||||
import 'package:gecko/widgets/cesium_avatar.dart';
|
||||
import 'package:gecko/widgets/datapod_avatar.dart';
|
||||
import 'package:graphql_flutter/graphql_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -82,7 +82,7 @@ class SearchIdentityQuery extends StatelessWidget {
|
|||
key: keySearchResult(profile['pubkey']),
|
||||
horizontalTitleGap: 10,
|
||||
contentPadding: const EdgeInsets.only(right: 2),
|
||||
leading: CesiumAvatar(
|
||||
leading: DatapodAvatar(
|
||||
address: profile['pubkey'], size: avatarSize),
|
||||
title: Row(children: <Widget>[
|
||||
Text(getShortPubkey(profile['pubkey']),
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'package:gecko/providers/substrate_sdk.dart';
|
|||
import 'package:gecko/providers/wallets_profiles.dart';
|
||||
import 'package:gecko/screens/wallet_view.dart';
|
||||
import 'package:gecko/widgets/balance.dart';
|
||||
import 'package:gecko/widgets/cesium_avatar.dart';
|
||||
import 'package:gecko/widgets/datapod_avatar.dart';
|
||||
import 'package:gecko/widgets/name_by_address.dart';
|
||||
import 'package:gecko/widgets/search_identity_query.dart';
|
||||
|
||||
|
@ -63,7 +63,7 @@ class SearchResult extends StatelessWidget {
|
|||
key: keySearchResult(g1Wallet.address),
|
||||
horizontalTitleGap: 10,
|
||||
contentPadding: const EdgeInsets.all(5),
|
||||
leading: CesiumAvatar(address: g1Wallet.address, size: avatarSize),
|
||||
leading: DatapodAvatar(address: g1Wallet.address, size: avatarSize),
|
||||
title: Row(children: <Widget>[
|
||||
Text(getShortPubkey(g1Wallet.address),
|
||||
style: scaledTextStyle(
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:gecko/globals.dart';
|
||||
import 'package:gecko/models/scale_functions.dart';
|
||||
import 'package:gecko/models/widgets_keys.dart';
|
||||
import 'package:gecko/providers/cesium_plus.dart';
|
||||
import 'package:gecko/providers/duniter_indexer.dart';
|
||||
import 'package:gecko/providers/substrate_sdk.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:gecko/screens/wallet_view.dart';
|
||||
import 'package:gecko/widgets/page_route_no_transition.dart';
|
||||
|
||||
|
@ -40,7 +40,7 @@ class TransactionTile extends StatelessWidget {
|
|||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
|
||||
leading: ClipOval(
|
||||
child: defaultAvatar(avatarSize),
|
||||
child: V2sDatapodProvider().defaultAvatar(avatarSize),
|
||||
),
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 5),
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:gecko/models/scale_functions.dart';
|
|||
import 'package:gecko/models/wallet_data.dart';
|
||||
import 'package:gecko/models/widgets_keys.dart';
|
||||
import 'package:gecko/providers/my_wallets.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:gecko/screens/myWallets/wallet_options.dart';
|
||||
import 'package:gecko/widgets/balance.dart';
|
||||
import 'package:gecko/widgets/commons/smooth_transition.dart';
|
||||
|
@ -24,6 +25,8 @@ class WalletTile extends StatelessWidget {
|
|||
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
|
||||
final defaultWallet = myWalletProvider.getDefaultWallet();
|
||||
|
||||
repository.getDatapodAvatar();
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.all(scaleSize(11)),
|
||||
child: GestureDetector(
|
||||
|
@ -50,39 +53,41 @@ class WalletTile extends StatelessWidget {
|
|||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: Column(children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
radius: 0.8,
|
||||
colors: [
|
||||
Color.fromARGB(255, 255, 255, 211),
|
||||
yellowC,
|
||||
],
|
||||
Expanded(child: Consumer<V2sDatapodProvider>(
|
||||
builder: (context, datapod, _) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
radius: 0.8,
|
||||
colors: [
|
||||
Color.fromARGB(255, 255, 255, 211),
|
||||
yellowC,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
child: repository.imageCustomPath == null ||
|
||||
repository.imageCustomPath == ''
|
||||
? Image.asset(
|
||||
'assets/avatars/${repository.imageDefaultPath}',
|
||||
alignment: Alignment.bottomCenter,
|
||||
scale: 0.5,
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.fitHeight,
|
||||
image: FileImage(
|
||||
File(repository.imageCustomPath!),
|
||||
child: repository.imageCustomPath == null ||
|
||||
repository.imageCustomPath == ''
|
||||
? Image.asset(
|
||||
'assets/avatars/${repository.imageDefaultPath}',
|
||||
alignment: Alignment.bottomCenter,
|
||||
scale: 0.5,
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.fitHeight,
|
||||
image: FileImage(
|
||||
File(repository.imageCustomPath!),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
})),
|
||||
Stack(children: <Widget>[
|
||||
BalanceBuilder(
|
||||
address: repository.address,
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:gecko/models/scale_functions.dart';
|
|||
import 'package:gecko/models/wallet_data.dart';
|
||||
import 'package:gecko/models/widgets_keys.dart';
|
||||
import 'package:gecko/providers/my_wallets.dart';
|
||||
import 'package:gecko/providers/v2s_datapod.dart';
|
||||
import 'package:gecko/screens/myWallets/wallet_options.dart';
|
||||
import 'package:gecko/widgets/balance.dart';
|
||||
import 'package:gecko/widgets/certifications.dart';
|
||||
|
@ -22,6 +23,9 @@ class WalletTileMembre extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
final myWalletProvider = Provider.of<MyWalletsProvider>(context);
|
||||
final defaultWallet = myWalletProvider.getDefaultWallet();
|
||||
|
||||
repository.getDatapodAvatar();
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: scaleSize(52), vertical: scaleSize(15)),
|
||||
|
@ -53,38 +57,41 @@ class WalletTileMembre extends StatelessWidget {
|
|||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
radius: 0.8,
|
||||
colors: [
|
||||
Color.fromARGB(255, 255, 255, 211),
|
||||
yellowC,
|
||||
],
|
||||
Consumer<V2sDatapodProvider>(
|
||||
builder: (context, datapod, _) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
radius: 0.8,
|
||||
colors: [
|
||||
Color.fromARGB(255, 255, 255, 211),
|
||||
yellowC,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
child: repository.imageCustomPath == null ||
|
||||
repository.imageCustomPath == ''
|
||||
? Image.asset(
|
||||
'assets/avatars/${repository.imageDefaultPath}',
|
||||
alignment: Alignment.bottomCenter,
|
||||
scale: 0.5,
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.fitHeight,
|
||||
image: FileImage(
|
||||
File(repository.imageCustomPath!),
|
||||
child: repository.imageCustomPath == null ||
|
||||
repository.imageCustomPath == ''
|
||||
? Image.asset(
|
||||
'assets/avatars/${repository.imageDefaultPath}',
|
||||
alignment: Alignment.bottomCenter,
|
||||
scale: 0.5,
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.transparent,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.fitHeight,
|
||||
image: FileImage(
|
||||
File(repository.imageCustomPath!),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
Positioned(
|
||||
left: 20,
|
||||
top: 20,
|
||||
|
|
Loading…
Reference in New Issue