feat: Added tx page
This commit is contained in:
parent
f0e6dcfbbe
commit
f472db8e00
@ -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<DomainsPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchDomains();
|
||||
}
|
||||
|
||||
List<Domain> domains = [];
|
||||
|
||||
Future<void> 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<DomainsPage> {
|
||||
body: Center(
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
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<DomainsPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
|
@ -21,12 +21,15 @@ class IndexPage extends StatefulWidget {
|
||||
class _IndexPageState extends State<IndexPage> {
|
||||
late String wallet;
|
||||
List<String> walletNames = [];
|
||||
late double walletBalance;
|
||||
bool balanceLoaded = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
wallet = widget.wallet;
|
||||
fetchWalletNames();
|
||||
fetchWalletBalance();
|
||||
}
|
||||
|
||||
Future<void> fetchWalletNames() async {
|
||||
@ -108,19 +111,53 @@ class _IndexPageState extends State<IndexPage> {
|
||||
void setWallet(String s) {
|
||||
setState(() {
|
||||
wallet = s;
|
||||
balanceLoaded = false;
|
||||
});
|
||||
widget.setWallet(s);
|
||||
}
|
||||
|
||||
int getWalletBalance() {
|
||||
return 100;
|
||||
Future<void> 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<void>(
|
||||
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(
|
||||
|
@ -31,6 +31,7 @@ Future<void> main() async {
|
||||
|
||||
// Get the uuid from the shared preferences
|
||||
final String? uuid = prefs.getString('uuid');
|
||||
print('UUID: $uuid');
|
||||
runApp(MyApp(uuid: uuid ?? 'null'));
|
||||
}
|
||||
|
||||
|
@ -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<TransactionsPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fetchTransactions();
|
||||
}
|
||||
|
||||
List<Transaction> transactions = [];
|
||||
|
||||
Future<void> 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<dynamic>).map<TXInput>((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<dynamic>).map<TXOutput>((output) {
|
||||
return TXOutput(
|
||||
address: output['address'],
|
||||
value: output['value'],
|
||||
covenant: Covenant(
|
||||
action: output['covenant']['action'],
|
||||
type: output['covenant']['type'],
|
||||
items: List<String>.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<TransactionsPage> {
|
||||
body: Center(
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
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<TransactionsPage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> 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<TXInput> inputs;
|
||||
final List<TXOutput> 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<String> 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;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
@ -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"))
|
||||
}
|
||||
|
64
pubspec.lock
64
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:
|
||||
|
@ -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:
|
||||
|
@ -6,6 +6,9 @@
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
Loading…
Reference in New Issue
Block a user