mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-02-02 11:16:36 +00:00
Merge branch 'litescribe' into origin/ordinals
This commit is contained in:
commit
cdddf5adc5
13 changed files with 400 additions and 106 deletions
39
lib/dto/ordinals/address_inscription_response.dart
Normal file
39
lib/dto/ordinals/address_inscription_response.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
import 'package:stackwallet/dto/ordinals/litescribe_response.dart';
|
||||
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
|
||||
|
||||
class AddressInscriptionResponse extends LitescribeResponse<AddressInscriptionResponse> {
|
||||
final int status;
|
||||
final String message;
|
||||
final AddressInscriptionResult result;
|
||||
|
||||
AddressInscriptionResponse({
|
||||
required this.status,
|
||||
required this.message,
|
||||
required this.result,
|
||||
});
|
||||
|
||||
factory AddressInscriptionResponse.fromJson(Map<String, dynamic> json) {
|
||||
return AddressInscriptionResponse(
|
||||
status: json['status'] as int,
|
||||
message: json['message'] as String,
|
||||
result: AddressInscriptionResult.fromJson(json['result'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddressInscriptionResult {
|
||||
final List<InscriptionData> list;
|
||||
final int total;
|
||||
|
||||
AddressInscriptionResult({
|
||||
required this.list,
|
||||
required this.total,
|
||||
});
|
||||
|
||||
factory AddressInscriptionResult.fromJson(Map<String, dynamic> json) {
|
||||
return AddressInscriptionResult(
|
||||
list: (json['list'] as List).map((item) => InscriptionData.fromJson(item as Map<String, dynamic>)).toList(),
|
||||
total: json['total'] as int,
|
||||
);
|
||||
}
|
||||
}
|
53
lib/dto/ordinals/inscription_data.dart
Normal file
53
lib/dto/ordinals/inscription_data.dart
Normal file
|
@ -0,0 +1,53 @@
|
|||
// inscription data from litescribe /address/inscriptions endpoint
|
||||
class InscriptionData {
|
||||
final String inscriptionId;
|
||||
final int inscriptionNumber;
|
||||
final String address;
|
||||
final String preview;
|
||||
final String content;
|
||||
final int contentLength;
|
||||
final String contentType;
|
||||
final String contentBody;
|
||||
final int timestamp;
|
||||
final String genesisTransaction;
|
||||
final String location;
|
||||
final String output;
|
||||
final int outputValue;
|
||||
final int offset;
|
||||
|
||||
InscriptionData({
|
||||
required this.inscriptionId,
|
||||
required this.inscriptionNumber,
|
||||
required this.address,
|
||||
required this.preview,
|
||||
required this.content,
|
||||
required this.contentLength,
|
||||
required this.contentType,
|
||||
required this.contentBody,
|
||||
required this.timestamp,
|
||||
required this.genesisTransaction,
|
||||
required this.location,
|
||||
required this.output,
|
||||
required this.outputValue,
|
||||
required this.offset,
|
||||
});
|
||||
|
||||
factory InscriptionData.fromJson(Map<String, dynamic> json) {
|
||||
return InscriptionData(
|
||||
inscriptionId: json['inscriptionId'] as String,
|
||||
inscriptionNumber: json['inscriptionNumber'] as int,
|
||||
address: json['address'] as String,
|
||||
preview: json['preview'] as String,
|
||||
content: json['content'] as String,
|
||||
contentLength: json['contentLength'] as int,
|
||||
contentType: json['contentType'] as String,
|
||||
contentBody: json['contentBody'] as String,
|
||||
timestamp: json['timestamp'] as int,
|
||||
genesisTransaction: json['genesisTransaction'] as String,
|
||||
location: json['location'] as String,
|
||||
output: json['output'] as String,
|
||||
outputValue: json['outputValue'] as int,
|
||||
offset: json['offset'] as int,
|
||||
);
|
||||
}
|
||||
}
|
6
lib/dto/ordinals/litescribe_response.dart
Normal file
6
lib/dto/ordinals/litescribe_response.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
class LitescribeResponse<T> {
|
||||
final T? data;
|
||||
final String? error;
|
||||
|
||||
LitescribeResponse({this.data, this.error});
|
||||
}
|
|
@ -1,26 +1,29 @@
|
|||
enum OrdCollection {
|
||||
punks,
|
||||
moonbirds,
|
||||
}
|
||||
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
|
||||
|
||||
class Ordinal {
|
||||
final String name;
|
||||
final String inscription;
|
||||
final String rank;
|
||||
final OrdCollection collection;
|
||||
final String inscriptionId;
|
||||
final int inscriptionNumber;
|
||||
final String content;
|
||||
|
||||
// following two are used to look up the UTXO object in isar combined w/ walletId
|
||||
final String utxoTXID;
|
||||
final int utxoVOUT;
|
||||
|
||||
// TODO: make a proper Isar class instead of this placeholder
|
||||
|
||||
Ordinal({
|
||||
required this.name,
|
||||
required this.inscription,
|
||||
required this.rank,
|
||||
required this.collection,
|
||||
required this.inscriptionId,
|
||||
required this.inscriptionNumber,
|
||||
required this.content,
|
||||
required this.utxoTXID,
|
||||
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]),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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/pages/ordinals/widgets/dialogs.dart';
|
||||
|
@ -17,10 +18,10 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
|
|||
|
||||
class OrdinalDetailsView extends StatefulWidget {
|
||||
const OrdinalDetailsView({
|
||||
super.key,
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
required this.ordinal,
|
||||
});
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final Ordinal ordinal;
|
||||
|
@ -28,7 +29,7 @@ class OrdinalDetailsView extends StatefulWidget {
|
|||
static const routeName = "/ordinalDetailsView";
|
||||
|
||||
@override
|
||||
State<OrdinalDetailsView> createState() => _OrdinalDetailsViewState();
|
||||
_OrdinalDetailsViewState createState() => _OrdinalDetailsViewState();
|
||||
}
|
||||
|
||||
class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
|
||||
|
@ -40,10 +41,10 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
|
|||
child: SafeArea(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
leading: const AppBarBackButton(),
|
||||
title: Text(
|
||||
"Ordinal details",
|
||||
|
@ -67,14 +68,14 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
|
|||
),
|
||||
_DetailsItemWCopy(
|
||||
title: "Inscription number",
|
||||
data: widget.ordinal.inscription,
|
||||
data: widget.ordinal.inscriptionNumber.toString(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: _spacing,
|
||||
),
|
||||
_DetailsItemWCopy(
|
||||
title: "Rank",
|
||||
data: widget.ordinal.rank,
|
||||
title: "ID",
|
||||
data: widget.ordinal.inscriptionId,
|
||||
),
|
||||
const SizedBox(
|
||||
height: _spacing,
|
||||
|
@ -83,23 +84,23 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
|
|||
const SizedBox(
|
||||
height: _spacing,
|
||||
),
|
||||
_DetailsItemWCopy(
|
||||
const _DetailsItemWCopy(
|
||||
title: "Amount",
|
||||
data: "FIXME",
|
||||
data: "TODO", // TODO infer from utxo utxoTXID:utxoVOUT
|
||||
),
|
||||
const SizedBox(
|
||||
height: _spacing,
|
||||
),
|
||||
_DetailsItemWCopy(
|
||||
const _DetailsItemWCopy(
|
||||
title: "Owner address",
|
||||
data: "FIXME",
|
||||
data: "TODO", // infer from address associated w utxoTXID
|
||||
),
|
||||
const SizedBox(
|
||||
height: _spacing,
|
||||
),
|
||||
_DetailsItemWCopy(
|
||||
title: "Transaction ID",
|
||||
data: "FIXME",
|
||||
data: widget.ordinal.utxoTXID,
|
||||
),
|
||||
const SizedBox(
|
||||
height: _spacing,
|
||||
|
@ -116,10 +117,10 @@ class _OrdinalDetailsViewState extends State<OrdinalDetailsView> {
|
|||
|
||||
class _DetailsItemWCopy extends StatelessWidget {
|
||||
const _DetailsItemWCopy({
|
||||
super.key,
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.data,
|
||||
});
|
||||
}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
final String data;
|
||||
|
@ -153,7 +154,7 @@ class _DetailsItemWCopy extends StatelessWidget {
|
|||
child: SvgPicture.asset(
|
||||
Assets.svg.copy,
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.infoItemIcons,
|
||||
Theme.of(context).extension<StackColors>()!.infoItemIcons,
|
||||
width: 12,
|
||||
),
|
||||
),
|
||||
|
@ -174,10 +175,10 @@ class _DetailsItemWCopy extends StatelessWidget {
|
|||
|
||||
class _OrdinalImageGroup extends StatelessWidget {
|
||||
const _OrdinalImageGroup({
|
||||
super.key,
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
required this.ordinal,
|
||||
});
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final Ordinal ordinal;
|
||||
|
@ -190,17 +191,25 @@ class _OrdinalImageGroup extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
ordinal.name,
|
||||
style: STextStyles.w600_16(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: _spacing,
|
||||
),
|
||||
// Text(
|
||||
// "${ordinal.inscriptionId}", // Use any other property you want
|
||||
// style: STextStyles.w600_16(context),
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: _spacing,
|
||||
// ),
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
child: Image.network(
|
||||
ordinal.content, // Use the preview URL as the image source
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.none, // Set the filter mode to nearest
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -14,6 +14,8 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stackwallet/models/ordinal.dart';
|
||||
import 'package:stackwallet/pages/ordinals/ordinals_filter_view.dart';
|
||||
import 'package:stackwallet/pages/ordinals/widgets/ordinals_list.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/services/mixins/ordinals_interface.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
|
@ -44,14 +46,24 @@ class _OrdinalsViewState extends ConsumerState<OrdinalsView> {
|
|||
late final FocusNode searchFocus;
|
||||
|
||||
String _searchTerm = "";
|
||||
dynamic _manager;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
searchController = TextEditingController();
|
||||
searchFocus = FocusNode();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
// Set _manager here when the widget's dependencies change
|
||||
_manager = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(widget.walletId)));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
searchController.dispose();
|
||||
|
@ -87,8 +99,8 @@ class _OrdinalsViewState extends ConsumerState<OrdinalsView> {
|
|||
.extension<StackColors>()!
|
||||
.topNavIconPrimary,
|
||||
),
|
||||
onPressed: () {
|
||||
// todo refresh
|
||||
onPressed: () async {
|
||||
(_manager.wallet as OrdinalsInterface).refreshInscriptions();
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -181,14 +193,7 @@ class _OrdinalsViewState extends ConsumerState<OrdinalsView> {
|
|||
Expanded(
|
||||
child: OrdinalsList(
|
||||
walletId: widget.walletId,
|
||||
ordinals: [
|
||||
for (int i = 0; i < 13; i++)
|
||||
Ordinal(
|
||||
name: "dummy name $i",
|
||||
inscription: "insc$i",
|
||||
rank: "r$i",
|
||||
),
|
||||
],
|
||||
ordinalsFuture: (_manager.wallet as OrdinalsInterface).getOrdinals(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:stackwallet/models/ordinal.dart';
|
||||
import 'package:stackwallet/pages/ordinals/ordinal_details_view.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
|
@ -6,10 +7,10 @@ import 'package:stackwallet/widgets/rounded_white_container.dart';
|
|||
|
||||
class OrdinalCard extends StatelessWidget {
|
||||
const OrdinalCard({
|
||||
super.key,
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
required this.ordinal,
|
||||
});
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final Ordinal ordinal;
|
||||
|
@ -27,27 +28,27 @@ class OrdinalCard extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"replace red container with image",
|
||||
),
|
||||
child: Image.network(
|
||||
ordinal.content, // Use the preview URL as the image source
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.none, // Set the filter mode to nearest
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
ordinal.name,
|
||||
'INSC. ${ordinal.inscriptionNumber}', // infer from address associated with utxoTXID
|
||||
style: STextStyles.w500_12(context),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
"INSC. ${ordinal.inscription} RANK ${ordinal.rank}",
|
||||
style: STextStyles.w500_8(context),
|
||||
),
|
||||
// const Spacer(),
|
||||
// Text(
|
||||
// "ID ${ordinal.inscriptionId}",
|
||||
// style: STextStyles.w500_8(context),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,39 +1,49 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/models/ordinal.dart';
|
||||
|
||||
import 'package:stackwallet/pages/ordinals/widgets/ordinal_card.dart';
|
||||
|
||||
class OrdinalsList extends StatefulWidget {
|
||||
class OrdinalsList extends StatelessWidget {
|
||||
const OrdinalsList({
|
||||
super.key,
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
required this.ordinals,
|
||||
});
|
||||
required this.ordinalsFuture,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final List<Ordinal> ordinals;
|
||||
final Future<List<Ordinal>> ordinalsFuture;
|
||||
|
||||
@override
|
||||
State<OrdinalsList> createState() => _OrdinalsListState();
|
||||
}
|
||||
|
||||
class _OrdinalsListState extends State<OrdinalsList> {
|
||||
static const spacing = 10.0;
|
||||
get spacing => 2.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.ordinals.length,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisSpacing: spacing,
|
||||
mainAxisSpacing: spacing,
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 3 / 4,
|
||||
),
|
||||
itemBuilder: (_, i) => OrdinalCard(
|
||||
walletId: widget.walletId,
|
||||
ordinal: widget.ordinals[i],
|
||||
),
|
||||
return FutureBuilder<List<Ordinal>>(
|
||||
future: ordinalsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasError) {
|
||||
return Text('Error: ${snapshot.error}');
|
||||
} else if (snapshot.hasData) {
|
||||
final List<Ordinal> ordinals = snapshot.data!;
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: ordinals.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisSpacing: spacing as double,
|
||||
mainAxisSpacing: spacing as double,
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 6 / 7, // was 3/4, less data displayed now
|
||||
),
|
||||
itemBuilder: (_, i) => OrdinalCard(
|
||||
walletId: walletId,
|
||||
ordinal: ordinals[i],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const Text('No data found.');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,13 +12,15 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
|
||||
import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart';
|
||||
import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart';
|
||||
import 'package:stackwallet/models/buy/response_objects/quote.dart';
|
||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/models/ordinal.dart';
|
||||
import 'package:stackwallet/models/ordinal.dart'; // TODO generalize InscriptionData -> Ordinal
|
||||
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||
import 'package:stackwallet/pages/add_wallet_views/add_token_view/add_custom_token_view.dart';
|
||||
|
@ -168,8 +170,6 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/widgets/choose_coin_view.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'models/isar/models/contact_entry.dart';
|
||||
|
||||
/*
|
||||
* This file contains all the routes for the app.
|
||||
* To add a new route, add it to the switch statement in the generateRoute method.
|
||||
|
|
|
@ -136,6 +136,7 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
_secureStore = secureStore;
|
||||
initCache(walletId, coin);
|
||||
initWalletDB(mockableOverride: mockableOverride);
|
||||
initOrdinalsInterface(walletId:walletId, coin: coin, db: db);
|
||||
initCoinControlInterface(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
|
|
68
lib/services/litescribe_api.dart
Normal file
68
lib/services/litescribe_api.dart
Normal file
|
@ -0,0 +1,68 @@
|
|||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import 'package:stackwallet/dto/ordinals/inscription_data.dart';
|
||||
import 'package:stackwallet/dto/ordinals/litescribe_response.dart';
|
||||
|
||||
class LitescribeAPI {
|
||||
static final LitescribeAPI _instance = LitescribeAPI._internal();
|
||||
|
||||
factory LitescribeAPI({required String baseUrl}) {
|
||||
_instance.baseUrl = baseUrl;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
LitescribeAPI._internal();
|
||||
|
||||
late String baseUrl;
|
||||
|
||||
Future<LitescribeResponse> _getResponse(String endpoint) async {
|
||||
final response = await http.get(Uri.parse('$baseUrl$endpoint'));
|
||||
if (response.statusCode == 200) {
|
||||
return LitescribeResponse(data: _validateJson(response.body));
|
||||
} else {
|
||||
throw Exception('LitescribeAPI _getResponse exception: Failed to load data');
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _validateJson(String responseBody) {
|
||||
final parsed = jsonDecode(responseBody);
|
||||
if (parsed is Map<String, dynamic>) {
|
||||
return parsed;
|
||||
} else {
|
||||
throw const FormatException('LitescribeAPI _validateJson exception: Invalid JSON format');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<InscriptionData>> getInscriptionsByAddress(String address, {int cursor = 0, int size = 1000}) async {
|
||||
// size param determines how many inscriptions are returned per response
|
||||
// default of 1000 is used to cover most addresses (I assume)
|
||||
// if the total number of inscriptions at the address exceeds the length of the list of inscriptions returned, another call with a higher size is made
|
||||
final int defaultLimit = 1000;
|
||||
final response = await _getResponse('/address/inscriptions?address=$address&cursor=$cursor&size=$size');
|
||||
|
||||
// Check if the number of returned inscriptions equals the limit
|
||||
final list = response.data['result']['list'];
|
||||
final int total = response.data['result']['total'] as int;
|
||||
final int currentSize = list.length as int;
|
||||
|
||||
if (currentSize == size && currentSize < total) {
|
||||
// If the number of returned inscriptions equals the limit and there are more inscriptions available,
|
||||
// increment the cursor and make the next API call to fetch the remaining inscriptions.
|
||||
final int newCursor = cursor + size;
|
||||
return getInscriptionsByAddress(address, cursor: newCursor, size: size);
|
||||
|
||||
} else {
|
||||
try {
|
||||
// Iterate through the list and create InscriptionData objects from each element
|
||||
final List<InscriptionData> inscriptions = (list as List<dynamic>)
|
||||
.map((json) => InscriptionData.fromJson(json as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
return inscriptions;
|
||||
} catch (e) {
|
||||
throw const FormatException('LitescribeAPI getInscriptionsByAddress exception: AddressInscriptionResponse.fromJson failure');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,111 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/db/isar/main_db.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 {
|
||||
// TODO wallet ordinals functionality
|
||||
}
|
||||
late final String _walletId;
|
||||
late final Coin _coin;
|
||||
late final MainDB _db;
|
||||
|
||||
void initOrdinalsInterface({
|
||||
required String walletId,
|
||||
required Coin coin,
|
||||
required MainDB db,
|
||||
}) {
|
||||
print('init');
|
||||
_walletId = walletId;
|
||||
_coin = coin;
|
||||
_db = db;
|
||||
}
|
||||
final LitescribeAPI litescribeAPI = LitescribeAPI(baseUrl: 'https://litescribe.io/api');
|
||||
|
||||
void refreshInscriptions() async {
|
||||
List<dynamic> _inscriptions;
|
||||
final utxos = await _db.getUTXOs(_walletId).findAll();
|
||||
final uniqueAddresses = getUniqueAddressesFromUTXOs(utxos);
|
||||
_inscriptions = await getInscriptionDataFromAddresses(uniqueAddresses);
|
||||
// TODO save inscriptions to isar which gets watched by a FutureBuilder/StreamBuilder
|
||||
}
|
||||
|
||||
Future<List<InscriptionData>> getInscriptionData() async {
|
||||
try {
|
||||
final utxos = await _db.getUTXOs(_walletId).findAll();
|
||||
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) {
|
||||
final Set<String> uniqueAddresses = <String>{};
|
||||
for (var utxo in utxos) {
|
||||
if (utxo.address != null) {
|
||||
uniqueAddresses.add(utxo.address!);
|
||||
}
|
||||
}
|
||||
return uniqueAddresses.toList();
|
||||
}
|
||||
|
||||
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 = [];
|
||||
for (String address in addresses) {
|
||||
try {
|
||||
var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
|
||||
allInscriptions.addAll(inscriptions);
|
||||
} catch (e) {
|
||||
print("Error fetching inscriptions for address $address: $e");
|
||||
}
|
||||
}
|
||||
return allInscriptions;
|
||||
}
|
||||
|
||||
Future<List<Ordinal>> getOrdinalsFromAddress(String address) async {
|
||||
try {
|
||||
var inscriptions = await litescribeAPI.getInscriptionsByAddress(address);
|
||||
return inscriptions.map((data) => Ordinal.fromInscriptionData(data)).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Error in OrdinalsInterface getOrdinalsFromAddress: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Ordinal>> getOrdinalsFromAddresses(List<String> addresses) async {
|
||||
List<Ordinal> allOrdinals = [];
|
||||
for (String address in addresses) {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import 'package:stackwallet/models/ordinal.dart';
|
||||
|
||||
class OrdinalsAPI {
|
||||
// dummy class with sample functions to be changed / filled out
|
||||
|
||||
static Future<List<Ordinal>> fetch() async {
|
||||
return [];
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue