diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart index 2ad4b18eb..48b307bab 100644 --- a/lib/pages/monkey/monkey_view.dart +++ b/lib/pages/monkey/monkey_view.dart @@ -4,11 +4,12 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/services/coins/banano/banano_wallet.dart'; +import 'package:stackwallet/services/monkey_service.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -44,75 +45,14 @@ class _MonkeyViewState extends ConsumerState { late final String walletId; List? imageBytes; - String receivingAddress = ""; - - Future getMonkeyImage(String address) async { - if (address.isEmpty) { - //address shouldn't be empty - return; - } - - final http.Response response = await http - .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); - - if (response.statusCode == 200) { - final manager = - ref.read(walletsChangeNotifierProvider).getManager(walletId); - final decodedResponse = response.bodyBytes; - await (manager.wallet as BananoWallet) - .updateMonkeyImageBytes(decodedResponse); - } else { - throw Exception("Failed to get MonKey"); - } + Future _updateWalletMonKey(Uint8List monKeyBytes) async { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(walletId); + await (manager.wallet as BananoWallet) + .updateMonkeyImageBytes(monKeyBytes.toList()); } - // void getMonkeySVG(String address) async { - // if (address.isEmpty) { - // //address shouldn't be empty - // return; - // } - // - // final http.Response response = await http - // .get(Uri.parse('https://monkey.banano.cc/api/v1/monkey/$address')); - // - // if (response.statusCode == 200) { - // final decodedResponse = response.bodyBytes; - // Directory directory = await getApplicationDocumentsDirectory(); - // late Directory sampleFolder; - // - // if (Platform.isAndroid) { - // directory = Directory("/storage/emulated/0/"); - // sampleFolder = Directory('${directory!.path}Documents'); - // } else if (Platform.isIOS) { - // sampleFolder = Directory(directory!.path); - // } else if (Platform.isLinux) { - // sampleFolder = Directory('${directory!.path}Documents'); - // } else if (Platform.isWindows) { - // sampleFolder = Directory('${directory!.path}Documents'); - // } else if (Platform.isMacOS) { - // sampleFolder = Directory('${directory!.path}Documents'); - // } - // - // try { - // if (!sampleFolder.existsSync()) { - // sampleFolder.createSync(recursive: true); - // } - // } catch (e, s) { - // // todo: come back to this - // debugPrint("$e $s"); - // } - // - // final docPath = sampleFolder.path; - // final filePath = "$docPath/monkey.svg"; - // - // File imgFile = File(filePath); - // await imgFile.writeAsBytes(decodedResponse); - // } else { - // throw Exception("Failed to get MonKey"); - // } - // } - - Future getDocsDir() async { + Future _getDocsDir() async { try { if (Platform.isAndroid) { return Directory("/storage/emulated/0/Documents"); @@ -124,80 +64,45 @@ class _MonkeyViewState extends ConsumerState { } } - Future downloadMonkey(String address, bool isPNG) async { - if (address.isEmpty) { - //address shouldn't be empty - return; + Future _saveMonKeyToFile({ + required Uint8List bytes, + bool isPNG = false, + bool overwrite = false, + }) async { + if (Platform.isAndroid) { + await Permission.storage.request(); } - String url = "https://monkey.banano.cc/api/v1/monkey/$address"; - - if (isPNG) { - url += '?format=png&size=512&background=false'; + final dir = await _getDocsDir(); + if (dir == null) { + throw Exception("Failed to get documents directory to save monKey image"); } - final http.Response response = await http.get(Uri.parse(url)); + final address = await ref + .read(walletsChangeNotifierProvider) + .getManager(walletId) + .currentReceivingAddress; + final docPath = dir.path; + String filePath = "$docPath/monkey_$address"; - if (response.statusCode == 200) { - if (Platform.isAndroid) { - await Permission.storage.request(); - } + filePath += isPNG ? ".png" : ".svg"; - final decodedResponse = response.bodyBytes; - final Directory? sampleFolder = await getDocsDir(); + File imgFile = File(filePath); - print("PATH: ${sampleFolder?.path}"); - - if (sampleFolder == null) { - print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); - return; - } - - // try { - // if (!sampleFolder.existsSync()) { - // sampleFolder.createSync(recursive: true); - // } - // } catch (e, s) { - // // todo: come back to this - // debugPrint("$e $s"); - // } - - final docPath = sampleFolder.path; - String filePath = "$docPath/monkey_$address"; - - filePath += isPNG ? ".png" : ".svg"; - - // todo check if monkey.png exists - - File imgFile = File(filePath); - await imgFile.writeAsBytes(decodedResponse); - } else { - throw Exception("Failed to get MonKey"); + if (imgFile.existsSync() && !overwrite) { + throw Exception("File already exists"); } + + await imgFile.writeAsBytes(bytes); } @override void initState() { walletId = widget.walletId; - WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - final address = await ref - .read(walletsChangeNotifierProvider) - .getManager(walletId) - .currentReceivingAddress; - setState(() { - receivingAddress = address; - }); - }); - super.initState(); } - @override - void dispose() { - super.dispose(); - } - @override Widget build(BuildContext context) { final manager = ref.watch(walletsChangeNotifierProvider @@ -318,22 +223,24 @@ class _MonkeyViewState extends ConsumerState { AspectRatio( aspectRatio: 1, child: AppBarIconButton( - icon: SvgPicture.asset( - Assets.svg.circleQuestion, - ), - onPressed: () { - showDialog( - context: context, - useSafeArea: false, - barrierDismissible: true, - builder: (context) { - return const StackOkDialog( - title: "About MonKeys", - message: - "A MonKey is a visual representation of your Banano address.", - ); - }); - }), + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + ), + onPressed: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackOkDialog( + title: "About MonKeys", + message: + "A MonKey is a visual representation of your Banano address.", + ); + }, + ); + }, + ), ), ], ), @@ -376,26 +283,95 @@ class _MonkeyViewState extends ConsumerState { SecondaryButton( label: "Save as SVG", onPressed: () async { + bool didError = false; await showLoading( - whileFuture: - downloadMonkey(receivingAddress, false), + whileFuture: Future.wait([ + _saveMonKeyToFile( + bytes: Uint8List.fromList( + (manager.wallet as BananoWallet) + .getMonkeyImageBytes()!), + ), + Future.delayed( + const Duration(seconds: 2), + ), + ]), context: context, isDesktop: Util.isDesktop, message: "Saving MonKey svg", + onException: (e) { + didError = true; + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, ); + + if (!didError && mounted) { + await showFloatingFlushBar( + type: FlushBarType.success, + message: "SVG MonKey image saved", + context: context, + ); + } }, ), const SizedBox(height: 12), SecondaryButton( label: "Download as PNG", onPressed: () async { + bool didError = false; await showLoading( - whileFuture: - downloadMonkey(receivingAddress, true), + whileFuture: Future.wait([ + manager.currentReceivingAddress.then( + (address) async => await ref + .read(pMonKeyService) + .fetchMonKey( + address: address, + png: true, + ) + .then( + (monKeyBytes) async => + await _saveMonKeyToFile( + bytes: monKeyBytes, + isPNG: true, + ), + ), + ), + Future.delayed( + const Duration(seconds: 2)), + ]), context: context, isDesktop: Util.isDesktop, message: "Downloading MonKey png", + onException: (e) { + didError = true; + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, ); + + if (!didError && mounted) { + await showFloatingFlushBar( + type: FlushBarType.success, + message: "PNG MonKey image saved", + context: context, + ); + } }, ), ], @@ -453,17 +429,37 @@ class _MonkeyViewState extends ConsumerState { child: PrimaryButton( label: "Fetch MonKey", onPressed: () async { - final future = Future.wait([ - getMonkeyImage(receivingAddress), - Future.delayed(const Duration(seconds: 2)), - ]); - await showLoading( - whileFuture: future, + whileFuture: Future.wait([ + manager.currentReceivingAddress.then( + (address) async => await ref + .read(pMonKeyService) + .fetchMonKey(address: address) + .then( + (monKeyBytes) async => + await _updateWalletMonKey( + monKeyBytes, + ), + ), + ), + Future.delayed(const Duration(seconds: 2)), + ]), context: context, isDesktop: Util.isDesktop, message: "Fetching MonKey", subMessage: "We are fetching your MonKey", + onException: (e) { + String msg = e.toString(); + while (msg.isNotEmpty && + msg.startsWith("Exception:")) { + msg = msg.substring(10).trim(); + } + showFloatingFlushBar( + type: FlushBarType.warning, + message: msg, + context: context, + ); + }, ); imageBytes = (manager.wallet as BananoWallet) @@ -472,17 +468,6 @@ class _MonkeyViewState extends ConsumerState { if (imageBytes != null) { setState(() {}); } - - // if (isDesktop) { - // Navigator.of(context).popUntil( - // ModalRoute.withName( - // DesktopWalletView.routeName), - // ); - // } else { - // Navigator.of(context).popUntil( - // ModalRoute.withName(WalletView.routeName), - // ); - // } }, ), ), diff --git a/lib/services/monkey_service.dart b/lib/services/monkey_service.dart new file mode 100644 index 000000000..46dfbb0ef --- /dev/null +++ b/lib/services/monkey_service.dart @@ -0,0 +1,40 @@ +import 'dart:typed_data'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:http/http.dart' as http; +import 'package:stackwallet/utilities/logger.dart'; + +final pMonKeyService = Provider((ref) => MonKeyService()); + +class MonKeyService { + static const baseURL = "https://monkey.banano.cc/api/v1/monkey/"; + + Future fetchMonKey({ + required String address, + bool png = false, + }) async { + try { + String url = "https://monkey.banano.cc/api/v1/monkey/$address"; + + if (png) { + url += '?format=png&size=512&background=false'; + } + + final response = await http.get(Uri.parse(url)); + + if (response.statusCode == 200) { + return response.bodyBytes; + } else { + throw Exception( + "statusCode=${response.statusCode} body=${response.body}", + ); + } + } catch (e, s) { + Logging.instance.log( + "Failed fetchMonKey($address): $e\n$s", + level: LogLevel.Error, + ); + rethrow; + } + } +}