diff --git a/lib/domains.dart b/lib/domains.dart index 3b3b973..5acde3c 100644 --- a/lib/domains.dart +++ b/lib/domains.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; class DomainsPage extends StatefulWidget { DomainsPage({Key? key, required this.uuid, required this.wallet}) @@ -15,6 +17,55 @@ class _DomainsPageState extends State { @override void initState() { super.initState(); + fetchDomains(); + } + + List domains = []; + + Future fetchDomains() async { + // Fetch domains from api + final response = await http.get(Uri.parse( + 'https://api.firewallet.au/wallet/domains?uuid=${widget.uuid}&name=${widget.wallet}')); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + if (data is Map && data.containsKey('error')) { + print('Error: ${data['error']}'); + return; + } + // Return if empty + if (data.isEmpty) { + setState(() { + domains = [ + Domain( + height: 0, + highest: 0, + name: 'No domains found', + state: 'CLOSED', + stats: DomainStats(unParsed: 'No domains found'), + value: 0) + ]; + }); + + return; + } + + final domainsData = data as List; + setState(() { + domains = domainsData + .map((domain) => Domain( + name: domain['name'], + state: domain['state'], + height: domain['height'], + highest: domain['highest'], + stats: DomainStats(unParsed: domain['stats'].toString()), + value: domain['value'], + )) + .toList(); + }); + } else { + // Handle error + print('Failed to load wallet names'); + } } @override @@ -27,12 +78,18 @@ class _DomainsPageState extends State { body: Center( child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, + return ListView( children: [ - Text("Domain 1"), - Text("Domain 2"), - Text("Domain 3"), + ...domains.map((name) { + return Card( + child: ListTile( + title: Text(name.name), + subtitle: Text(name.state), + trailing: Text(name.value.toString()), + ), + ); + }), + TextButton(onPressed: fetchDomains, child: Text('Refresh')), ], ); }, @@ -41,3 +98,27 @@ class _DomainsPageState extends State { ); } } + +class Domain { + final String name; + final String state; + final int height; + final double highest; + final DomainStats stats; + final double value; + + Domain({ + required this.name, + required this.state, + required this.height, + required this.highest, + required this.stats, + required this.value, + }); +} + +class DomainStats { + final String unParsed; + + DomainStats({required this.unParsed}); +} diff --git a/lib/home.dart b/lib/home.dart index d37de88..3703fb1 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -21,12 +21,15 @@ class IndexPage extends StatefulWidget { class _IndexPageState extends State { late String wallet; List walletNames = []; + late double walletBalance; + bool balanceLoaded = false; @override void initState() { super.initState(); wallet = widget.wallet; fetchWalletNames(); + fetchWalletBalance(); } Future fetchWalletNames() async { @@ -108,19 +111,53 @@ class _IndexPageState extends State { void setWallet(String s) { setState(() { wallet = s; + balanceLoaded = false; }); widget.setWallet(s); } - int getWalletBalance() { - return 100; + Future fetchWalletBalance() async { + if (wallet.isEmpty) { + walletBalance = 0; + return; + } + + if (balanceLoaded) { + return; + } + + final response = await http.get(Uri.parse( + 'https://api.firewallet.au/wallet/balance?uuid=${widget.uuid}&name=$wallet')); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + print(data); + + setState(() { + balanceLoaded = true; + walletBalance = data['available']; + }); + } else { + // Handle error + print('Failed to load wallet balance'); + } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('$wallet - ${getWalletBalance()} HNS'), + title: FutureBuilder( + future: fetchWalletBalance(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Text('$wallet - Loading...'); + } else if (snapshot.hasError) { + return Text('$wallet - Error'); + } else { + return Text('$wallet - ${walletBalance.toStringAsFixed(2)} HNS'); + } + }, + ), ), body: Center( child: LayoutBuilder( diff --git a/lib/main.dart b/lib/main.dart index 1ec2acc..6d4c0ea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,6 +31,7 @@ Future main() async { // Get the uuid from the shared preferences final String? uuid = prefs.getString('uuid'); + print('UUID: $uuid'); runApp(MyApp(uuid: uuid ?? 'null')); } diff --git a/lib/transactions.dart b/lib/transactions.dart index 0891cc3..bde9a60 100644 --- a/lib/transactions.dart +++ b/lib/transactions.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:url_launcher/url_launcher.dart'; class TransactionsPage extends StatefulWidget { TransactionsPage({Key? key, required this.uuid, required this.wallet}) @@ -15,6 +18,87 @@ class _TransactionsPageState extends State { @override void initState() { super.initState(); + fetchTransactions(); + } + + List transactions = []; + + Future fetchTransactions() async { + // Fetch domains from api + final response = await http.get(Uri.parse( + 'https://api.firewallet.au/wallet/transactions?uuid=${widget.uuid}&name=${widget.wallet}')); + if (response.statusCode == 200) { + final data = jsonDecode(response.body); + if (data is Map && data.containsKey('error')) { + print('Error: ${data['error']}'); + return; + } + // Return if empty + if (data.isEmpty) { + setState(() { + transactions = [ + Transaction( + confirmations: 0, + date: 'No transactions found', + hash: 'No transactions found', + inputs: [], + outputs: []) + ]; + }); + + return; + } + + final txData = data as List; + setState(() { + transactions = txData + .map( + (tx) => Transaction( + confirmations: tx['confirmations'], + date: tx['date'], + hash: tx['hash'], + inputs: (tx['inputs'] as List).map((input) { + return TXInput( + address: input['address'], + value: input['value'], + path: input['path'] == null + ? Path() + : Path( + account: input['path']['account'], + change: input['path']['change'], + derivation: input['path']['derivation'], + name: input['path']['name'], + )); + }).toList(), + outputs: + (tx['outputs'] as List).map((output) { + return TXOutput( + address: output['address'], + value: output['value'], + covenant: Covenant( + action: output['covenant']['action'], + type: output['covenant']['type'], + items: List.from(output['covenant']['items'])), + path: output['path'] == null + ? Path() + : Path( + account: output['path']['account'], + change: output['path']['change'], + derivation: output['path']['derivation'], + name: output['path']['name'], + ), + ); + }).toList(), + ), + ) + .toList(); + }); + print(transactions[0].hash); + print(transactions[0].value().toStringAsFixed(2)); + } else { + // Handle error + print('Failed to load wallet names'); + } } @override @@ -27,12 +111,20 @@ class _TransactionsPageState extends State { body: Center( child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, + return ListView( children: [ - Text("Domain 1"), - Text("Domain 2"), - Text("Domain 3"), + TextButton( + onPressed: fetchTransactions, child: Text('Refresh')), + ...transactions.map((tx) { + return Card( + child: ListTile( + title: Text(tx.toString()), + subtitle: Text(tx.hash), + trailing: Text('${tx.value().toStringAsFixed(2)} HNS'), + onTap: // Open tx in browser + () => openTX(tx.hash)), + ); + }), ], ); }, @@ -40,4 +132,200 @@ class _TransactionsPageState extends State { ), ); } + + Future openTX(String hash) async { + // Open the transaction in the browser + final Uri url = Uri.parse('https://flutter.dev'); + if (!await launchUrl(url)) { + throw Exception('Could not launch $url'); + } + } +} + +class Transaction { + final int confirmations; + final String date; + final String hash; + final List inputs; + final List outputs; + double totalValue = 0; + bool valueCalculated = false; + + Transaction( + {required this.confirmations, + required this.date, + required this.hash, + required this.inputs, + required this.outputs}); + + // Calculate the total value of the transaction + double value() { + if (valueCalculated) { + return totalValue / 1000000; + } + totalValue = inputs.fold(0, (prev, input) => prev + input.getValue()); + totalValue += outputs.fold(0, (prev, output) => prev + output.getValue()); + valueCalculated = true; + return totalValue / 1000000; + } + + @override + String toString() { + if (inputs.isEmpty) { + return 'No Transactions Found'; + } + + int opens = 0; + int bids = 0; + int reveals = 0; + int redeems = 0; + int registers = 0; + int updates = 0; + int transfers = 0; + int finalizes = 0; + int renews = 0; + int other = 0; + + outputs.forEach((output) { + if (output.covenant.action == 'OPEN') { + opens++; + } else if (output.covenant.action == 'BID') { + bids++; + } else if (output.covenant.action == 'REVEAL') { + reveals++; + } else if (output.covenant.action == 'REDEEM') { + redeems++; + } else if (output.covenant.action == 'REGISTER') { + registers++; + } else if (output.covenant.action == 'UPDATE') { + updates++; + } else if (output.covenant.action == 'TRANSFER') { + transfers++; + } else if (output.covenant.action == 'FINALIZE') { + finalizes++; + } else if (output.covenant.action == 'RENEW') { + renews++; + } else if (output.covenant.action == 'NONE') { + other++; + } else { + other++; + print('Unknown action: ${output.covenant.action}'); + } + }); + + String outputString = ''; + if (opens > 0) { + outputString += 'Opens: $opens, '; + } + if (bids > 0) { + outputString += 'Bids: $bids, '; + } + if (reveals > 0) { + outputString += 'Reveals: $reveals, '; + } + if (redeems > 0) { + outputString += 'Redeems: $redeems, '; + } + if (registers > 0) { + outputString += 'Registers: $registers, '; + } + if (updates > 0) { + outputString += 'Updates: $updates, '; + } + + if (transfers > 0) { + outputString += 'Transfers: $transfers, '; + } + + if (finalizes > 0) { + outputString += 'Finalizes: $finalizes, '; + } + + if (renews > 0) { + outputString += 'Renews: $renews, '; + } + + // Remove trailing comma + if (outputString.isNotEmpty) { + outputString = outputString.substring(0, outputString.length - 2); + } + + if (outputString.isEmpty) { + outputString = 'Sent HNS'; + } + + return outputString; + } +} + +class TXInput { + final String address; + final int value; + final Path path; + + TXInput({required this.address, required this.value, required this.path}); + + int getValue() { + if (path.userPath()) { + return value * -1; + } + return value; + } +} + +class TXOutput { + final String address; + final int value; + final Covenant covenant; + final Path path; + + TXOutput( + {required this.address, + required this.value, + required this.covenant, + required this.path}); + + int getValue() { + if (path.userPath()) { + if (covenant.action == 'BID') { + return 0; + } + + return value; + } + return 0; + } +} + +class Covenant { + final String action; + final int type; + final List items; + + Covenant({required this.action, required this.type, required this.items}); +} + +class Path { + int? account; + bool? change; + String? derivation; + String? name; + + Path({this.account, this.change, this.derivation, this.name}); + + @override + String toString() { + if (name == null) { + return 'No Path Found'; + } + if (derivation == null) { + return 'Path(name: $name)'; + } + + return 'Path(account: $account, change: $change, derivation: $derivation, name: $name)'; + } + + bool userPath() { + return derivation != null; + } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..f6f23bf 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..f16b4c3 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 724bb2a..997e35d 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index c0f6365..3c83b1f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -405,6 +405,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" + url: "https://pub.dev" + source: hosted + version: "6.2.6" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: ceb2625f0c24ade6ef6778d1de0b2e44f2db71fded235eb52295247feba8c5cf + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" + source: hosted + version: "3.1.1" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ef34380..04fdc36 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: cupertino_icons: ^1.0.6 shared_preferences: ^2.2.3 http: ^1.2.1 + url_launcher: ^6.2.6 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..4f78848 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..88b22e5 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST