convert/map/cast InscriptionData->Ordinal

This commit is contained in:
sneurlax 2023-07-21 11:48:31 -05:00
parent ddba1c54f7
commit f46a37d4d4
7 changed files with 99 additions and 144 deletions

View file

@ -1,26 +1,29 @@
enum OrdCollection { import 'package:stackwallet/dto/ordinals/inscription_data.dart';
punks,
moonbirds,
}
class Ordinal { class Ordinal {
final String name; final String inscriptionId;
final String inscription; final int inscriptionNumber;
final String rank; final String content;
final OrdCollection collection;
// following two are used to look up the UTXO object in isar combined w/ walletId // following two are used to look up the UTXO object in isar combined w/ walletId
final String utxoTXID; final String utxoTXID;
final int utxoVOUT; final int utxoVOUT;
// TODO: make a proper Isar class instead of this placeholder
Ordinal({ Ordinal({
required this.name, required this.inscriptionId,
required this.inscription, required this.inscriptionNumber,
required this.rank, required this.content,
required this.collection,
required this.utxoTXID, required this.utxoTXID,
required this.utxoVOUT, required this.utxoVOUT,
}); });
}
factory Ordinal.fromInscriptionData(InscriptionData data) {
return Ordinal(
inscriptionId: data.inscriptionId,
inscriptionNumber: data.inscriptionNumber,
content: data.content,
utxoTXID: data.output.split(':')[0], // "output": "062f32e21aa04246b8873b5d9a929576addd0339881e1ea478b406795d6b6c47:0"
utxoVOUT: int.parse(data.output.split(':')[1]),
);
}
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/dto/ordinals/inscription_data.dart';
import 'package:stackwallet/models/ordinal.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart'; import 'package:stackwallet/pages/ordinals/widgets/dialogs.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
@ -15,16 +16,15 @@ import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
class OrdinalDetailsView extends StatefulWidget { class OrdinalDetailsView extends StatefulWidget {
const OrdinalDetailsView({ const OrdinalDetailsView({
Key? key, Key? key,
required this.walletId, required this.walletId,
required this.inscriptionData, required this.ordinal,
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final InscriptionData inscriptionData; final Ordinal ordinal;
static const routeName = "/ordinalDetailsView"; static const routeName = "/ordinalDetailsView";
@ -62,20 +62,20 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
horizontal: 39, horizontal: 39,
), ),
child: _OrdinalImageGroup( child: _OrdinalImageGroup(
inscriptionData: widget.inscriptionData, ordinal: widget.ordinal,
walletId: widget.walletId, walletId: widget.walletId,
), ),
), ),
_DetailsItemWCopy( _DetailsItemWCopy(
title: "Inscription number", title: "Inscription number",
data: widget.inscriptionData.inscriptionNumber.toString(), data: widget.ordinal.inscriptionNumber.toString(),
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
_DetailsItemWCopy( _DetailsItemWCopy(
title: "ID", title: "ID",
data: widget.inscriptionData.inscriptionId, data: widget.ordinal.inscriptionId,
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
@ -84,23 +84,23 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
_DetailsItemWCopy( const _DetailsItemWCopy(
title: "Amount", title: "Amount",
data: "${widget.inscriptionData.outputValue}", data: "TODO", // TODO infer from utxo utxoTXID:utxoVOUT
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
_DetailsItemWCopy( const _DetailsItemWCopy(
title: "Owner address", title: "Owner address",
data: widget.inscriptionData.address, data: "TODO", // infer from address associated w utxoTXID
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
), ),
_DetailsItemWCopy( _DetailsItemWCopy(
title: "Transaction ID", title: "Transaction ID",
data: widget.inscriptionData.genesisTransaction, data: widget.ordinal.utxoTXID,
), ),
const SizedBox( const SizedBox(
height: _spacing, height: _spacing,
@ -177,11 +177,11 @@ class _OrdinalImageGroup extends StatelessWidget {
const _OrdinalImageGroup({ const _OrdinalImageGroup({
Key? key, Key? key,
required this.walletId, required this.walletId,
required this.inscriptionData, required this.ordinal,
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final InscriptionData inscriptionData; final Ordinal ordinal;
static const _spacing = 12.0; static const _spacing = 12.0;
@ -192,7 +192,7 @@ class _OrdinalImageGroup extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Text( Text(
"${inscriptionData.inscriptionId}", // Use any other property you want "${ordinal.inscriptionId}", // Use any other property you want
style: STextStyles.w600_16(context), style: STextStyles.w600_16(context),
), ),
const SizedBox( const SizedBox(
@ -205,7 +205,7 @@ class _OrdinalImageGroup extends StatelessWidget {
child: Container( child: Container(
color: Colors.red, color: Colors.red,
child: Image.network( child: Image.network(
inscriptionData.content, // Use the preview URL as the image source ordinal.content, // Use the preview URL as the image source
fit: BoxFit.cover, fit: BoxFit.cover,
filterQuality: FilterQuality.none, // Set the filter mode to nearest filterQuality: FilterQuality.none, // Set the filter mode to nearest
), ),

View file

@ -193,7 +193,7 @@ class _OrdinalsViewState extends ConsumerState<OrdinalsView> {
Expanded( Expanded(
child: OrdinalsList( child: OrdinalsList(
walletId: widget.walletId, walletId: widget.walletId,
ordinalsFuture: (_manager.wallet as OrdinalsInterface).getInscriptions(), ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(),
), ),
), ),
], ],

View file

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
import 'package:stackwallet/models/ordinal.dart'; // TODO generalize InscriptionData models -> Ordinal import 'package:stackwallet/models/ordinal.dart';
import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart'; import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -9,11 +9,11 @@ class OrdinalCard extends StatelessWidget {
const OrdinalCard({ const OrdinalCard({
Key? key, Key? key,
required this.walletId, required this.walletId,
required this.inscriptionData, required this.ordinal,
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final InscriptionData inscriptionData; final Ordinal ordinal;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -22,7 +22,7 @@ class OrdinalCard extends StatelessWidget {
onPressed: () { onPressed: () {
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
OrdinalDetailsView.routeName, OrdinalDetailsView.routeName,
arguments: (walletId: walletId, inscriptionData: inscriptionData), arguments: (walletId: walletId, ordinal: ordinal),
); );
}, },
child: Column( child: Column(
@ -33,7 +33,7 @@ class OrdinalCard extends StatelessWidget {
child: Container( child: Container(
color: Colors.red, color: Colors.red,
child: Image.network( child: Image.network(
inscriptionData.content, // Use the preview URL as the image source ordinal.content, // Use the preview URL as the image source
fit: BoxFit.cover, fit: BoxFit.cover,
filterQuality: FilterQuality.none, // Set the filter mode to nearest filterQuality: FilterQuality.none, // Set the filter mode to nearest
), ),
@ -41,12 +41,12 @@ class OrdinalCard extends StatelessWidget {
), ),
const Spacer(), const Spacer(),
Text( Text(
inscriptionData.address, 'TODO', // infer from address associated with utxoTXID
style: STextStyles.w500_12(context), style: STextStyles.w500_12(context),
), ),
const Spacer(), const Spacer(),
Text( Text(
"INSC. ${inscriptionData.inscriptionNumber} ID ${inscriptionData.inscriptionId}", "INSC. ${ordinal.inscriptionNumber} ID ${ordinal.inscriptionId}",
style: STextStyles.w500_8(context), style: STextStyles.w500_8(context),
), ),
], ],

View file

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/models/ordinal.dart';
import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart'; import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart';
@ -11,13 +11,13 @@ class OrdinalsList extends StatelessWidget {
}) : super(key: key); }) : super(key: key);
final String walletId; final String walletId;
final Future<List<InscriptionData>> ordinalsFuture; final Future<List<Ordinal>> ordinalsFuture;
get spacing => 2.0; get spacing => 2.0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<List<InscriptionData>>( return FutureBuilder<List<Ordinal>>(
future: ordinalsFuture, future: ordinalsFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
@ -25,10 +25,10 @@ class OrdinalsList extends StatelessWidget {
} else if (snapshot.hasError) { } else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}'); return Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
final List<InscriptionData> inscriptions = snapshot.data!; final List<Ordinal> ordinals = snapshot.data!;
return GridView.builder( return GridView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: inscriptions.length, itemCount: ordinals.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: spacing as double, crossAxisSpacing: spacing as double,
mainAxisSpacing: spacing as double, mainAxisSpacing: spacing as double,
@ -37,7 +37,7 @@ class OrdinalsList extends StatelessWidget {
), ),
itemBuilder: (_, i) => OrdinalCard( itemBuilder: (_, i) => OrdinalCard(
walletId: walletId, walletId: walletId,
inscriptionData: inscriptions[i], ordinal: ordinals[i],
), ),
); );
} else { } else {

View file

@ -423,12 +423,12 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}"); return _routeError("${settings.name} invalid args: ${args.toString()}");
case OrdinalDetailsView.routeName: case OrdinalDetailsView.routeName:
if (args is ({InscriptionData inscriptionData, String walletId})) { if (args is ({Ordinal ordinal, String walletId})) {
return getRoute( return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute, shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => OrdinalDetailsView( builder: (_) => OrdinalDetailsView(
walletId: args.walletId, walletId: args.walletId,
inscriptionData: args.inscriptionData, ordinal: args.ordinal,
), ),
settings: RouteSettings( settings: RouteSettings(
name: settings.name, name: settings.name,

View file

@ -2,11 +2,11 @@ import 'dart:async';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/services/litescribe_api.dart';
import 'package:stackwallet/dto/ordinals/inscription_data.dart'; import 'package:stackwallet/dto/ordinals/inscription_data.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
import 'package:stackwallet/models/ordinal.dart';
import 'package:stackwallet/services/litescribe_api.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
mixin OrdinalsInterface { mixin OrdinalsInterface {
late final String _walletId; late final String _walletId;
@ -25,28 +25,32 @@ mixin OrdinalsInterface {
} }
final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api'); final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api');
// Future<List<InscriptionData>> getInscriptionsByAddress(String address) async {
// try {
// var response = await litescribeAPI.getInscriptionsByAddress(address);
// // print("Found ${response.result.total} inscription${response.result.total > 1 ? 's' : ''} at address $address"); // TODO disable (POC)
// return response.result.list;
// } catch (e) {
// throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e');
// }
// }
void refreshInscriptions() async { void refreshInscriptions() async {
List<dynamic> _inscriptions; List<dynamic> _inscriptions;
final utxos = await _db.getUTXOs(_walletId).findAll(); final utxos = await _db.getUTXOs(_walletId).findAll();
final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos);
_inscriptions = await getInscriptionsFromAddresses(uniqueAddresses); _inscriptions = await getInscriptionDataFromAddresses(uniqueAddresses);
// TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder // TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder
} }
Future<List<InscriptionData>> getInscriptions() async { Future<List<InscriptionData>> getInscriptionData() async {
final utxos = await _db.getUTXOs(_walletId).findAll(); try {
final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos); final utxos = await _db.getUTXOs(_walletId).findAll();
return await getInscriptionsFromAddresses(uniqueAddresses); final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos);
return await getInscriptionDataFromAddresses(uniqueAddresses);
} catch (e) {
throw Exception('Error in OrdinalsInterface getInscriptions: $e');
}
}
Future<List<Ordinal>> getOrdinals() async {
try {
final utxos = await _db.getUTXOs(_walletId).findAll();
final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos);
return await getOrdinalsFromAddresses(uniqueAddresses);
} catch (e) {
throw Exception('Error in OrdinalsInterface getOrdinals: $e');
}
} }
List<String> getUniqueAddressesFromUTXOs(List<UTXO> utxos) { List<String> getUniqueAddressesFromUTXOs(List<UTXO> utxos) {
@ -59,12 +63,22 @@ mixin OrdinalsInterface {
return uniqueAddresses.toList(); return uniqueAddresses.toList();
} }
Future<List<InscriptionData>> getInscriptionsFromAddresses(List<String> addresses) async { Future<List<InscriptionData>> getInscriptionDataFromAddress(String address) async {
List<InscriptionData> allInscriptions = [];
try {
var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
allInscriptions.addAll(inscriptions);
} catch (e) {
throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e');
}
return allInscriptions;
}
Future<List<InscriptionData>> getInscriptionDataFromAddresses(List<String> addresses) async {
List<InscriptionData> allInscriptions = []; List<InscriptionData> allInscriptions = [];
for (String address in addresses) { for (String address in addresses) {
try { try {
var inscriptions = await litescribeAPI.getInscriptionsByAddress(address); var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
print("Found ${inscriptions.length} inscription${inscriptions.length > 1 ? 's' : ''} at address $address");
allInscriptions.addAll(inscriptions); allInscriptions.addAll(inscriptions);
} catch (e) { } catch (e) {
print("Error fetching inscriptions for address $address: $e"); print("Error fetching inscriptions for address $address: $e");
@ -73,87 +87,25 @@ mixin OrdinalsInterface {
return allInscriptions; return allInscriptions;
} }
/* // ord-litecoin interface Future<List<Ordinal>> getOrdinalsFromAddress(String address) async {
final OrdinalsAPI ordinalsAPI = OrdinalsAPI(baseUrl: 'https://ord-litecoin.stackwallet.com');
Future<FeedResponse> fetchLatestInscriptions() async {
try { try {
final feedResponse = await ordinalsAPI.getLatestInscriptions(); var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
// Process the feedResponse data as needed return inscriptions.map((data) => Ordinal.fromInscriptionData(data)).toList();
// print('Latest Inscriptions:');
// for (var inscription in feedResponse.inscriptions) {
// print('Title: ${inscription.title}, Href: ${inscription.href}');
// }
return feedResponse;
} catch (e) { } catch (e) {
// Handle errors throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e');
throw Exception('Error in OrdinalsInterface fetchLatestInscriptions: $e');
} }
} }
Future<InscriptionResponse> getInscriptionDetails(String inscriptionId) async { Future<List<Ordinal>> getOrdinalsFromAddresses(List<String> addresses) async {
try { List<Ordinal> allOrdinals = [];
return await ordinalsAPI.getInscriptionDetails(inscriptionId); for (String address in addresses) {
} catch (e) { try {
throw Exception('Error in OrdinalsInterface getInscriptionDetails: $e'); var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
allOrdinals.addAll(inscriptions.map((data) => Ordinal.fromInscriptionData(data)));
} catch (e) {
print("Error fetching inscriptions for address $address: $e");
}
} }
return allOrdinals;
} }
Future<SatResponse> getSatDetails(int satNumber) async {
try {
return await ordinalsAPI.getSatDetails(satNumber);
} catch (e) {
throw Exception('Error in OrdinalsInterface getSatDetails: $e');
}
}
Future<TransactionResponse> getTransaction(String transactionId) async {
try {
print(1);
return await ordinalsAPI.getTransaction(transactionId);
} catch (e) {
throw Exception('Error in OrdinalsInterface getTransaction: $e');
}
}
Future<OutputResponse> getTransactionOutputs(String transactionId) async {
try {
return await ordinalsAPI.getTransactionOutputs(transactionId);
} catch (e) {
throw Exception('Error in OrdinalsInterface getTransactionOutputs: $e');
}
}
Future<AddressResponse> getInscriptionsByAddress(String address) async {
try {
return await ordinalsAPI.getInscriptionsByAddress(address);
} catch (e) {
throw Exception('Error in OrdinalsInterface getInscriptionsByAddress: $e');
}
}
Future<BlockResponse> getBlock(int blockNumber) async {
try {
return await ordinalsAPI.getBlock(blockNumber);
} catch (e) {
throw Exception('Error in OrdinalsInterface getBlock: $e');
}
}
Future<ContentResponse> getInscriptionContent(String inscriptionId) async {
try {
return await ordinalsAPI.getInscriptionContent(inscriptionId);
} catch (e) {
throw Exception('Error in OrdinalsInterface getInscriptionContent: $e');
}
}
Future<PreviewResponse> getInscriptionPreview(String inscriptionId) async {
try {
return await ordinalsAPI.getInscriptionPreview(inscriptionId);
} catch (e) {
throw Exception('Error in OrdinalsInterface getInscriptionPreview: $e');
}
}
*/ // /ord-litecoin interface
} }