mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 17:57:40 +00:00
commit
bd05d6dddf
13 changed files with 316 additions and 162 deletions
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/exceptions/main_db/main_db_exception.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
@ -40,64 +41,101 @@ class MainDB {
|
|||
String walletId) =>
|
||||
isar.addresses.where().walletIdEqualTo(walletId);
|
||||
|
||||
Future<void> putAddress(Address address) => isar.writeTxn(() async {
|
||||
await isar.addresses.put(address);
|
||||
Future<int> putAddress(Address address) async {
|
||||
try {
|
||||
return await isar.writeTxn(() async {
|
||||
return await isar.addresses.put(address);
|
||||
});
|
||||
} catch (e) {
|
||||
throw MainDBException("failed putAddress: $address", e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> putAddresses(List<Address> addresses) => isar.writeTxn(() async {
|
||||
await isar.addresses.putAll(addresses);
|
||||
Future<List<int>> putAddresses(List<Address> addresses) async {
|
||||
try {
|
||||
return await isar.writeTxn(() async {
|
||||
return await isar.addresses.putAll(addresses);
|
||||
});
|
||||
} catch (e) {
|
||||
throw MainDBException("failed putAddresses: $addresses", e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateOrPutAddresses(List<Address> addresses) async {
|
||||
await isar.writeTxn(() async {
|
||||
for (final address in addresses) {
|
||||
final storedAddress = await isar.addresses
|
||||
.getByValueWalletId(address.value, address.walletId);
|
||||
Future<List<int>> updateOrPutAddresses(List<Address> addresses) async {
|
||||
try {
|
||||
List<int> ids = [];
|
||||
await isar.writeTxn(() async {
|
||||
for (final address in addresses) {
|
||||
final storedAddress = await isar.addresses
|
||||
.getByValueWalletId(address.value, address.walletId);
|
||||
|
||||
if (storedAddress == null) {
|
||||
await isar.addresses.put(address);
|
||||
} else {
|
||||
address.id = storedAddress.id;
|
||||
await storedAddress.transactions.load();
|
||||
final txns = storedAddress.transactions.toList();
|
||||
await isar.addresses.delete(storedAddress.id);
|
||||
await isar.addresses.put(address);
|
||||
address.transactions.addAll(txns);
|
||||
await address.transactions.save();
|
||||
int id;
|
||||
if (storedAddress == null) {
|
||||
id = await isar.addresses.put(address);
|
||||
} else {
|
||||
address.id = storedAddress.id;
|
||||
await storedAddress.transactions.load();
|
||||
final txns = storedAddress.transactions.toList();
|
||||
await isar.addresses.delete(storedAddress.id);
|
||||
id = await isar.addresses.put(address);
|
||||
address.transactions.addAll(txns);
|
||||
await address.transactions.save();
|
||||
}
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return ids;
|
||||
} catch (e) {
|
||||
throw MainDBException("failed updateOrPutAddresses: $addresses", e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Address?> getAddress(String walletId, String address) async {
|
||||
return isar.addresses.getByValueWalletId(address, walletId);
|
||||
}
|
||||
|
||||
Future<void> updateAddress(Address oldAddress, Address newAddress) =>
|
||||
isar.writeTxn(() async {
|
||||
Future<int> updateAddress(Address oldAddress, Address newAddress) async {
|
||||
try {
|
||||
return await isar.writeTxn(() async {
|
||||
newAddress.id = oldAddress.id;
|
||||
await oldAddress.transactions.load();
|
||||
final txns = oldAddress.transactions.toList();
|
||||
await isar.addresses.delete(oldAddress.id);
|
||||
await isar.addresses.put(newAddress);
|
||||
final id = await isar.addresses.put(newAddress);
|
||||
newAddress.transactions.addAll(txns);
|
||||
await newAddress.transactions.save();
|
||||
return id;
|
||||
});
|
||||
} catch (e) {
|
||||
throw MainDBException(
|
||||
"failed updateAddress: from=$oldAddress to=$newAddress", e);
|
||||
}
|
||||
}
|
||||
|
||||
// transactions
|
||||
QueryBuilder<Transaction, Transaction, QAfterWhereClause> getTransactions(
|
||||
String walletId) =>
|
||||
isar.transactions.where().walletIdEqualTo(walletId);
|
||||
|
||||
Future<void> putTransaction(Transaction transaction) =>
|
||||
isar.writeTxn(() async {
|
||||
await isar.transactions.put(transaction);
|
||||
Future<int> putTransaction(Transaction transaction) async {
|
||||
try {
|
||||
return await isar.writeTxn(() async {
|
||||
return await isar.transactions.put(transaction);
|
||||
});
|
||||
} catch (e) {
|
||||
throw MainDBException("failed putTransaction: $transaction", e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> putTransactions(List<Transaction> transactions) =>
|
||||
isar.writeTxn(() async {
|
||||
await isar.transactions.putAll(transactions);
|
||||
Future<List<int>> putTransactions(List<Transaction> transactions) async {
|
||||
try {
|
||||
return await isar.writeTxn(() async {
|
||||
return await isar.transactions.putAll(transactions);
|
||||
});
|
||||
} catch (e) {
|
||||
throw MainDBException("failed putTransactions: $transactions", e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Transaction?> getTransaction(String walletId, String txid) async {
|
||||
return isar.transactions.getByTxidWalletId(txid, walletId);
|
||||
|
@ -212,55 +250,60 @@ class MainDB {
|
|||
}
|
||||
|
||||
Future<void> addNewTransactionData(
|
||||
List<Tuple4<Transaction, List<Output>, List<Input>, Address?>>
|
||||
transactionsData,
|
||||
String walletId) async {
|
||||
await isar.writeTxn(() async {
|
||||
for (final data in transactionsData) {
|
||||
final tx = data.item1;
|
||||
List<Tuple4<Transaction, List<Output>, List<Input>, Address?>>
|
||||
transactionsData,
|
||||
String walletId,
|
||||
) async {
|
||||
try {
|
||||
await isar.writeTxn(() async {
|
||||
for (final data in transactionsData) {
|
||||
final tx = data.item1;
|
||||
|
||||
final potentiallyUnconfirmedTx = await getTransactions(walletId)
|
||||
.filter()
|
||||
.txidEqualTo(tx.txid)
|
||||
.findFirst();
|
||||
if (potentiallyUnconfirmedTx != null) {
|
||||
// update use id to replace tx
|
||||
tx.id = potentiallyUnconfirmedTx.id;
|
||||
await isar.transactions.delete(potentiallyUnconfirmedTx.id);
|
||||
}
|
||||
// save transaction
|
||||
await isar.transactions.put(tx);
|
||||
|
||||
// link and save outputs
|
||||
if (data.item2.isNotEmpty) {
|
||||
await isar.outputs.putAll(data.item2);
|
||||
tx.outputs.addAll(data.item2);
|
||||
await tx.outputs.save();
|
||||
}
|
||||
|
||||
// link and save inputs
|
||||
if (data.item3.isNotEmpty) {
|
||||
await isar.inputs.putAll(data.item3);
|
||||
tx.inputs.addAll(data.item3);
|
||||
await tx.inputs.save();
|
||||
}
|
||||
|
||||
if (data.item4 != null) {
|
||||
final address = await getAddresses(walletId)
|
||||
final potentiallyUnconfirmedTx = await getTransactions(walletId)
|
||||
.filter()
|
||||
.valueEqualTo(data.item4!.value)
|
||||
.txidEqualTo(tx.txid)
|
||||
.findFirst();
|
||||
if (potentiallyUnconfirmedTx != null) {
|
||||
// update use id to replace tx
|
||||
tx.id = potentiallyUnconfirmedTx.id;
|
||||
await isar.transactions.delete(potentiallyUnconfirmedTx.id);
|
||||
}
|
||||
// save transaction
|
||||
await isar.transactions.put(tx);
|
||||
|
||||
// check if address exists in db and add if it does not
|
||||
if (address == null) {
|
||||
await isar.addresses.put(data.item4!);
|
||||
// link and save outputs
|
||||
if (data.item2.isNotEmpty) {
|
||||
await isar.outputs.putAll(data.item2);
|
||||
tx.outputs.addAll(data.item2);
|
||||
await tx.outputs.save();
|
||||
}
|
||||
|
||||
// link and save address
|
||||
tx.address.value = address ?? data.item4!;
|
||||
await tx.address.save();
|
||||
// link and save inputs
|
||||
if (data.item3.isNotEmpty) {
|
||||
await isar.inputs.putAll(data.item3);
|
||||
tx.inputs.addAll(data.item3);
|
||||
await tx.inputs.save();
|
||||
}
|
||||
|
||||
if (data.item4 != null) {
|
||||
final address = await getAddresses(walletId)
|
||||
.filter()
|
||||
.valueEqualTo(data.item4!.value)
|
||||
.findFirst();
|
||||
|
||||
// check if address exists in db and add if it does not
|
||||
if (address == null) {
|
||||
await isar.addresses.put(data.item4!);
|
||||
}
|
||||
|
||||
// link and save address
|
||||
tx.address.value = address ?? data.item4!;
|
||||
await tx.address.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
throw MainDBException("failed addNewTransactionData", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
lib/exceptions/main_db/main_db_exception.dart
Normal file
12
lib/exceptions/main_db/main_db_exception.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:stackwallet/exceptions/sw_exception.dart';
|
||||
|
||||
class MainDBException extends SWException {
|
||||
MainDBException(super.message, this.originalError);
|
||||
|
||||
final Object originalError;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "$message: originalError=$originalError";
|
||||
}
|
||||
}
|
|
@ -176,7 +176,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
children: [
|
||||
PayNymBot(
|
||||
paymentCodeString: widget.accountLite.code,
|
||||
size: 32,
|
||||
size: 36,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
|
@ -186,7 +186,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
children: [
|
||||
Text(
|
||||
widget.accountLite.nymName,
|
||||
style: STextStyles.w600_12(context),
|
||||
style: STextStyles.w600_14(context),
|
||||
),
|
||||
FutureBuilder(
|
||||
future:
|
||||
|
@ -204,7 +204,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
),
|
||||
Text(
|
||||
"Connected",
|
||||
style: STextStyles.w500_10(context)
|
||||
style: STextStyles.w500_12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -230,33 +230,33 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
if (snapshot.data!) {
|
||||
return PrimaryButton(
|
||||
label: "Send",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.circleArrowUpRight,
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 14,
|
||||
height: 14,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextPrimary,
|
||||
),
|
||||
iconSpacing: 4,
|
||||
width: 86,
|
||||
iconSpacing: 8,
|
||||
width: 100,
|
||||
onPressed: _onSend,
|
||||
);
|
||||
} else {
|
||||
return PrimaryButton(
|
||||
label: "Connect",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.circlePlusFilled,
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 13,
|
||||
height: 13,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextPrimary,
|
||||
),
|
||||
iconSpacing: 4,
|
||||
width: 86,
|
||||
iconSpacing: 8,
|
||||
width: 128,
|
||||
onPressed: _onConnectPressed,
|
||||
);
|
||||
}
|
||||
|
@ -291,6 +291,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.warningForeground,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -321,7 +322,9 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
children: [
|
||||
Text(
|
||||
"PayNym address",
|
||||
style: STextStyles.infoSmall(context),
|
||||
style: STextStyles.infoSmall(context).copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
|
@ -332,6 +335,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -346,7 +350,7 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
),
|
||||
QrImage(
|
||||
padding: const EdgeInsets.all(0),
|
||||
size: 86,
|
||||
size: 100,
|
||||
data: widget.accountLite.code,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
|
@ -375,16 +379,16 @@ class _PaynymDetailsPopupState extends ConsumerState<PaynymDetailsPopup> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Copy",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
iconSpacing: 8,
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.copy,
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
),
|
||||
iconSpacing: 4,
|
||||
onPressed: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
|
|
|
@ -56,7 +56,7 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
children: [
|
||||
PayNymBot(
|
||||
paymentCodeString: paynymAccount.codes.first.code,
|
||||
size: isDesktop ? 56 : 32,
|
||||
size: isDesktop ? 56 : 36,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
|
@ -65,7 +65,7 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
paynymAccount.nymName,
|
||||
style: isDesktop
|
||||
? STextStyles.w500_24(context)
|
||||
: STextStyles.w600_12(context),
|
||||
: STextStyles.w600_14(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -87,7 +87,7 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
children: [
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 107),
|
||||
constraints: const BoxConstraints(minHeight: 130),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
@ -100,7 +100,9 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
)
|
||||
: STextStyles.infoSmall(context),
|
||||
: STextStyles.infoSmall(context).copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
|
@ -113,6 +115,7 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -120,7 +123,7 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
),
|
||||
CustomTextButton(
|
||||
text: "Copy",
|
||||
textSize: isDesktop ? 18 : 10,
|
||||
textSize: isDesktop ? 18 : 14,
|
||||
onTap: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
|
@ -146,7 +149,7 @@ class PaynymQrPopup extends StatelessWidget {
|
|||
),
|
||||
QrImage(
|
||||
padding: const EdgeInsets.all(0),
|
||||
size: 107,
|
||||
size: 130,
|
||||
data: paynymAccount.codes.first.code,
|
||||
foregroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.textDark,
|
||||
|
|
|
@ -303,7 +303,9 @@ class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
|
|||
.code,
|
||||
12,
|
||||
5),
|
||||
style: STextStyles.label(context),
|
||||
style: STextStyles.label(context).copyWith(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 11,
|
||||
|
@ -313,11 +315,11 @@ class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Copy",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
iconSpacing: 4,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
iconSpacing: 8,
|
||||
icon: CopyIcon(
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
|
@ -350,11 +352,11 @@ class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Share",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
iconSpacing: 4,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
iconSpacing: 8,
|
||||
icon: ShareIcon(
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
|
@ -387,11 +389,11 @@ class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
|
|||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Address",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
iconSpacing: 4,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
iconSpacing: 8,
|
||||
icon: QrCodeIcon(
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
|
@ -554,7 +556,7 @@ class _PaynymHomeViewState extends ConsumerState<PaynymHomeView> {
|
|||
child: child,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: isDesktop ? 56 : 40,
|
||||
height: isDesktop ? 56 : 48,
|
||||
width: isDesktop ? 490 : null,
|
||||
child: Toggle(
|
||||
onColor: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
|
|
|
@ -171,7 +171,7 @@ class _PaynymDetailsPopupState extends ConsumerState<DesktopPaynymDetails> {
|
|||
children: [
|
||||
PayNymBot(
|
||||
paymentCodeString: widget.accountLite.code,
|
||||
size: 32,
|
||||
size: 36,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
|
|
|
@ -37,7 +37,7 @@ class _PaynymCardState extends State<PaynymCard> {
|
|||
child: Row(
|
||||
children: [
|
||||
PayNymBot(
|
||||
size: 32,
|
||||
size: 36,
|
||||
paymentCodeString: widget.paymentCodeString,
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -56,7 +56,7 @@ class _PaynymCardState extends State<PaynymCard> {
|
|||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
)
|
||||
: STextStyles.w500_12(context),
|
||||
: STextStyles.w500_14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
|
@ -65,7 +65,7 @@ class _PaynymCardState extends State<PaynymCard> {
|
|||
Format.shorten(widget.paymentCodeString, 12, 5),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
: STextStyles.w500_12(context).copyWith(
|
||||
: STextStyles.w500_14(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
|
|
|
@ -77,7 +77,7 @@ class _PaynymCardButtonState extends ConsumerState<PaynymCardButton> {
|
|||
child: Row(
|
||||
children: [
|
||||
PayNymBot(
|
||||
size: 32,
|
||||
size: 36,
|
||||
paymentCodeString: widget.accountLite.code,
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -96,7 +96,7 @@ class _PaynymCardButtonState extends ConsumerState<PaynymCardButton> {
|
|||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
)
|
||||
: STextStyles.w500_12(context),
|
||||
: STextStyles.w500_14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
|
@ -105,7 +105,7 @@ class _PaynymCardButtonState extends ConsumerState<PaynymCardButton> {
|
|||
Format.shorten(widget.accountLite.code, 12, 5),
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
: STextStyles.w500_12(context).copyWith(
|
||||
: STextStyles.w500_14(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:bech32/bech32.dart';
|
||||
import 'package:bip32/bip32.dart' as bip32;
|
||||
|
@ -980,6 +981,7 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId));
|
||||
await _checkCurrentReceivingAddressesForTransactions();
|
||||
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.4, walletId));
|
||||
await checkAllCurrentReceivingPaynymAddressesForTransactions();
|
||||
|
||||
final fetchFuture = _refreshTransactions();
|
||||
|
@ -2372,27 +2374,38 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
return transactionObject;
|
||||
}
|
||||
|
||||
final int vSizeForOneOutput = (await buildTransaction(
|
||||
utxosToUse: utxoObjectsToUse,
|
||||
utxoSigningData: utxoSigningData,
|
||||
recipients: [_recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
final int vSizeForTwoOutPuts = (await buildTransaction(
|
||||
utxosToUse: utxoObjectsToUse,
|
||||
utxoSigningData: utxoSigningData,
|
||||
recipients: [
|
||||
_recipientAddress,
|
||||
await _getCurrentAddressForChain(1, DerivePathTypeExt.primaryFor(coin)),
|
||||
],
|
||||
satoshiAmounts: [
|
||||
satoshiAmountToSend,
|
||||
// this can cause a problem where the output value is negative so commenting out for now
|
||||
// satoshisBeingUsed - satoshiAmountToSend - 1
|
||||
// and using dust limit instead
|
||||
DUST_LIMIT,
|
||||
], // dust limit is the minimum amount a change output should be
|
||||
))["vSize"] as int;
|
||||
final int vSizeForOneOutput;
|
||||
try {
|
||||
vSizeForOneOutput = (await buildTransaction(
|
||||
utxosToUse: utxoObjectsToUse,
|
||||
utxoSigningData: utxoSigningData,
|
||||
recipients: [_recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
} catch (e) {
|
||||
Logging.instance.log("vSizeForOneOutput: $e", level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
|
||||
final int vSizeForTwoOutPuts;
|
||||
try {
|
||||
vSizeForTwoOutPuts = (await buildTransaction(
|
||||
utxosToUse: utxoObjectsToUse,
|
||||
utxoSigningData: utxoSigningData,
|
||||
recipients: [
|
||||
_recipientAddress,
|
||||
await _getCurrentAddressForChain(
|
||||
1, DerivePathTypeExt.primaryFor(coin)),
|
||||
],
|
||||
satoshiAmounts: [
|
||||
satoshiAmountToSend,
|
||||
max(0, satoshisBeingUsed - satoshiAmountToSend - 1),
|
||||
],
|
||||
))["vSize"] as int;
|
||||
} catch (e) {
|
||||
Logging.instance.log("vSizeForTwoOutPuts: $e", level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
final feeForOneOutput = estimateTxFee(
|
||||
|
|
|
@ -148,15 +148,13 @@ mixin PaynymWalletInterface {
|
|||
btc_dart.NetworkType get networkType => _network;
|
||||
|
||||
Future<Address> currentReceivingPaynymAddress(PaymentCode sender) async {
|
||||
final key = await lookupKey(sender.toString());
|
||||
final keys = await lookupKey(sender.toString());
|
||||
final address = await _db
|
||||
.getAddresses(_walletId)
|
||||
.filter()
|
||||
.subTypeEqualTo(AddressSubType.paynymReceive)
|
||||
.and()
|
||||
.otherDataEqualTo(key)
|
||||
.and()
|
||||
.otherDataIsNotNull()
|
||||
.anyOf<String, Address>(keys, (q, String e) => q.otherDataEqualTo(e))
|
||||
.sortByDerivationIndexDesc()
|
||||
.findFirst();
|
||||
|
||||
|
@ -331,15 +329,13 @@ mixin PaynymWalletInterface {
|
|||
const maxCount = 2147483647;
|
||||
|
||||
for (int i = startIndex; i < maxCount; i++) {
|
||||
final key = await lookupKey(pCode.toString());
|
||||
final keys = await lookupKey(pCode.toString());
|
||||
final address = await _db
|
||||
.getAddresses(_walletId)
|
||||
.filter()
|
||||
.subTypeEqualTo(AddressSubType.paynymSend)
|
||||
.and()
|
||||
.otherDataEqualTo(key)
|
||||
.and()
|
||||
.otherDataIsNotNull()
|
||||
.anyOf<String, Address>(keys, (q, String e) => q.otherDataEqualTo(e))
|
||||
.and()
|
||||
.derivationIndexEqualTo(i)
|
||||
.findFirst();
|
||||
|
@ -1215,16 +1211,17 @@ mixin PaynymWalletInterface {
|
|||
}
|
||||
|
||||
/// look up a key that corresponds to a payment code string
|
||||
Future<String?> lookupKey(String paymentCodeString) async {
|
||||
Future<List<String>> lookupKey(String paymentCodeString) async {
|
||||
final keys =
|
||||
(await _secureStorage.keys).where((e) => e.startsWith(kPCodeKeyPrefix));
|
||||
final List<String> result = [];
|
||||
for (final key in keys) {
|
||||
final value = await _secureStorage.read(key: key);
|
||||
if (value == paymentCodeString) {
|
||||
return key;
|
||||
result.add(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// fetch a payment code string
|
||||
|
|
|
@ -13,31 +13,31 @@ class STextStyles {
|
|||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark3,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.oceanBreeze:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark3,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.dark:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark3,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.oledBlack:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark3,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.fruitSorbet:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark3,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
fontSize: 14,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -932,6 +932,76 @@ class STextStyles {
|
|||
}
|
||||
}
|
||||
|
||||
static TextStyle w600_14(BuildContext context) {
|
||||
switch (_theme(context).themeType) {
|
||||
case ThemeType.light:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.oceanBreeze:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.dark:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.oledBlack:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.fruitSorbet:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 14,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static TextStyle w500_14(BuildContext context) {
|
||||
switch (_theme(context).themeType) {
|
||||
case ThemeType.light:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.oceanBreeze:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.dark:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.oledBlack:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
);
|
||||
case ThemeType.fruitSorbet:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static TextStyle w500_12(BuildContext context) {
|
||||
switch (_theme(context).themeType) {
|
||||
case ThemeType.light:
|
||||
|
|
|
@ -272,8 +272,8 @@ class _PaynymFollowToggleButtonState
|
|||
switch (widget.style) {
|
||||
case PaynymFollowToggleButtonStyle.primary:
|
||||
return PrimaryButton(
|
||||
width: isDesktop ? 120 : 84,
|
||||
buttonHeight: isDesktop ? ButtonHeight.s : ButtonHeight.l,
|
||||
width: isDesktop ? 120 : 100,
|
||||
buttonHeight: isDesktop ? ButtonHeight.s : ButtonHeight.xl,
|
||||
label: isFollowing ? "Unfollow" : "Follow",
|
||||
onPressed: _onPressed,
|
||||
);
|
||||
|
@ -281,15 +281,15 @@ class _PaynymFollowToggleButtonState
|
|||
case PaynymFollowToggleButtonStyle.detailsPopup:
|
||||
return SecondaryButton(
|
||||
label: isFollowing ? "Unfollow" : "Follow",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
iconSpacing: 8,
|
||||
icon: SvgPicture.asset(
|
||||
isFollowing ? Assets.svg.userMinus : Assets.svg.userPlus,
|
||||
width: 10,
|
||||
height: 10,
|
||||
width: 16,
|
||||
height: 16,
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.buttonTextSecondary,
|
||||
),
|
||||
iconSpacing: 4,
|
||||
onPressed: _onPressed,
|
||||
);
|
||||
|
||||
|
|
|
@ -78,6 +78,16 @@ class SecondaryButton extends StatelessWidget {
|
|||
.buttonTextSecondaryDisabled,
|
||||
);
|
||||
}
|
||||
if (buttonHeight == ButtonHeight.xl) {
|
||||
return STextStyles.button(context).copyWith(
|
||||
fontSize: 14,
|
||||
color: enabled
|
||||
? Theme.of(context).extension<StackColors>()!.buttonTextSecondary
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondaryDisabled,
|
||||
);
|
||||
}
|
||||
return STextStyles.button(context).copyWith(
|
||||
color: enabled
|
||||
? Theme.of(context).extension<StackColors>()!.buttonTextSecondary
|
||||
|
|
Loading…
Reference in a new issue