mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-18 16:44:32 +00:00
Merge remote-tracking branch 'origin/staging' into hideex
This commit is contained in:
commit
38defea3d1
29 changed files with 744 additions and 156 deletions
Binary file not shown.
21
lib/models/keys/cw_key_data.dart
Normal file
21
lib/models/keys/cw_key_data.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'key_data_interface.dart';
|
||||
|
||||
class CWKeyData with KeyDataInterface {
|
||||
CWKeyData({
|
||||
required this.walletId,
|
||||
required String? privateSpendKey,
|
||||
required String? privateViewKey,
|
||||
required String? publicSpendKey,
|
||||
required String? publicViewKey,
|
||||
}) : keys = List.unmodifiable([
|
||||
(label: "Public View Key", key: publicViewKey),
|
||||
(label: "Private View Key", key: privateViewKey),
|
||||
(label: "Public Spend Key", key: publicSpendKey),
|
||||
(label: "Private Spend Key", key: privateSpendKey),
|
||||
]);
|
||||
|
||||
@override
|
||||
final String walletId;
|
||||
|
||||
final List<({String label, String key})> keys;
|
||||
}
|
3
lib/models/keys/key_data_interface.dart
Normal file
3
lib/models/keys/key_data_interface.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
mixin KeyDataInterface {
|
||||
String get walletId;
|
||||
}
|
17
lib/models/keys/xpriv_data.dart
Normal file
17
lib/models/keys/xpriv_data.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import 'key_data_interface.dart';
|
||||
|
||||
class XPrivData with KeyDataInterface {
|
||||
XPrivData({
|
||||
required this.walletId,
|
||||
required this.fingerprint,
|
||||
required List<XPriv> xprivs,
|
||||
}) : xprivs = List.unmodifiable(xprivs);
|
||||
|
||||
@override
|
||||
final String walletId;
|
||||
|
||||
final String fingerprint;
|
||||
|
||||
final List<XPriv> xprivs;
|
||||
}
|
|
@ -12,6 +12,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
|
||||
import '../../pages_desktop_specific/cashfusion/sub_widgets/fusion_progress.dart';
|
||||
import '../../providers/cash_fusion/fusion_progress_ui_state_provider.dart';
|
||||
|
@ -84,6 +85,8 @@ class _FusionProgressViewState extends ConsumerState<FusionProgressView> {
|
|||
message: "Stopping fusion",
|
||||
);
|
||||
|
||||
await Wakelock.disable();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -96,6 +99,12 @@ class _FusionProgressViewState extends ConsumerState<FusionProgressView> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
Wakelock.disable();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool _succeeded =
|
||||
|
@ -108,6 +117,8 @@ class _FusionProgressViewState extends ConsumerState<FusionProgressView> {
|
|||
.watch(fusionProgressUIStateProvider(widget.walletId))
|
||||
.fusionRoundsCompleted;
|
||||
|
||||
Wakelock.enable();
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
return await _requestAndProcessCancel();
|
||||
|
|
|
@ -175,7 +175,7 @@ class _FrostSendStep3State extends ConsumerState<FrostSendStep3> {
|
|||
PrimaryButton(
|
||||
label: "Generate transaction",
|
||||
enabled: _userVerifyContinue &&
|
||||
!fieldIsEmptyFlags.reduce((v, e) => v |= e),
|
||||
!fieldIsEmptyFlags.fold(false, (v, e) => v |= e),
|
||||
onPressed: () async {
|
||||
// collect Share strings
|
||||
final sharesCollected = controllers.map((e) => e.text).toList();
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../../models/keys/cw_key_data.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/clipboard_interface.dart';
|
||||
import '../../../../utilities/constants.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class CNWalletKeys extends StatefulWidget {
|
||||
const CNWalletKeys({
|
||||
super.key,
|
||||
required this.cwKeyData,
|
||||
required this.walletId,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
});
|
||||
|
||||
final CWKeyData cwKeyData;
|
||||
final String walletId;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
|
||||
@override
|
||||
State<CNWalletKeys> createState() => _CNWalletKeysState();
|
||||
}
|
||||
|
||||
class _CNWalletKeysState extends State<CNWalletKeys> {
|
||||
late String _currentDropDownValue;
|
||||
|
||||
String _current(String key) =>
|
||||
widget.cwKeyData.keys.firstWhere((e) => e.label == key).key;
|
||||
|
||||
Future<void> _copy() async {
|
||||
await widget.clipboardInterface.setData(
|
||||
ClipboardData(text: _current(_currentDropDownValue)),
|
||||
);
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_currentDropDownValue = widget.cwKeyData.keys.first.label;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: Util.isDesktop
|
||||
? const EdgeInsets.symmetric(horizontal: 20)
|
||||
: EdgeInsets.zero,
|
||||
child: Column(
|
||||
mainAxisSize: Util.isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 12 : 16,
|
||||
),
|
||||
DetailItemBase(
|
||||
horizontal: true,
|
||||
borderColor: Util.isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
|
||||
: null,
|
||||
title: Text(
|
||||
"Selected key",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
detail: SizedBox(
|
||||
width: Util.isDesktop ? 200 : 170,
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<String>(
|
||||
value: _currentDropDownValue,
|
||||
items: [
|
||||
...widget.cwKeyData.keys.map(
|
||||
(e) => DropdownMenuItem(
|
||||
value: e.label,
|
||||
child: Text(
|
||||
e.label,
|
||||
style: STextStyles.w500_14(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value is String) {
|
||||
setState(() {
|
||||
_currentDropDownValue = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
isExpanded: true,
|
||||
buttonStyleData: ButtonStyleData(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
iconStyleData: IconStyleData(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.chevronDown,
|
||||
width: 12,
|
||||
height: 6,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
),
|
||||
),
|
||||
dropdownStyleData: DropdownStyleData(
|
||||
offset: const Offset(0, -10),
|
||||
elevation: 0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
),
|
||||
menuItemStyleData: const MenuItemStyleData(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 12 : 16,
|
||||
),
|
||||
QR(
|
||||
data: _current(_currentDropDownValue),
|
||||
size:
|
||||
Util.isDesktop ? 256 : MediaQuery.of(context).size.width / 1.5,
|
||||
),
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 12 : 16,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
borderColor: Util.isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
|
||||
: null,
|
||||
child: SelectableText(
|
||||
_current(_currentDropDownValue),
|
||||
style: STextStyles.w500_14(context),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 12 : 16,
|
||||
),
|
||||
if (!Util.isDesktop) const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
if (Util.isDesktop) const Spacer(),
|
||||
if (Util.isDesktop)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Copy",
|
||||
onPressed: _copy,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -13,9 +13,11 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../models/keys/cw_key_data.dart';
|
||||
import '../../../../models/keys/key_data_interface.dart';
|
||||
import '../../../../models/keys/xpriv_data.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/address_utils.dart';
|
||||
|
@ -25,17 +27,19 @@ import '../../../../utilities/constants.dart';
|
|||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import '../../../../widgets/background.dart';
|
||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/custom_buttons/blue_text_button.dart';
|
||||
import '../../../../widgets/custom_buttons/simple_copy_button.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
import '../../../../widgets/stack_dialog.dart';
|
||||
import '../../../add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
|
||||
import '../../../wallet_view/transaction_views/transaction_details_view.dart';
|
||||
import 'cn_wallet_keys.dart';
|
||||
import 'wallet_xprivs.dart';
|
||||
|
||||
class WalletBackupView extends ConsumerWidget {
|
||||
|
@ -44,8 +48,7 @@ class WalletBackupView extends ConsumerWidget {
|
|||
required this.walletId,
|
||||
required this.mnemonic,
|
||||
this.frostWalletData,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
this.xprivData,
|
||||
this.keyData,
|
||||
});
|
||||
|
||||
static const String routeName = "/walletBackup";
|
||||
|
@ -58,8 +61,7 @@ class WalletBackupView extends ConsumerWidget {
|
|||
String keys,
|
||||
({String config, String keys})? prevGen,
|
||||
})? frostWalletData;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
final ({List<XPriv> xprivs, String fingerprint})? xprivData;
|
||||
final KeyDataInterface? keyData;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
|
@ -81,57 +83,29 @@ class WalletBackupView extends ConsumerWidget {
|
|||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
actions: [
|
||||
if (xprivData != null)
|
||||
if (keyData != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: CustomTextButton(
|
||||
text: "xpriv(s)",
|
||||
text: switch (keyData.runtimeType) {
|
||||
const (XPrivData) => "xpriv(s)",
|
||||
const (CWKeyData) => "keys",
|
||||
_ => throw UnimplementedError(
|
||||
"Don't forget to add your KeyDataInterface here! ${keyData.runtimeType}",
|
||||
),
|
||||
},
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
MobileXPrivsView.routeName,
|
||||
MobileKeyDataView.routeName,
|
||||
arguments: (
|
||||
walletId: walletId,
|
||||
xprivData: xprivData!,
|
||||
keyData: keyData!,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (!frost && xprivData == null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: AppBarIconButton(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
shadows: const [],
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.copy,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.topNavIconPrimary,
|
||||
),
|
||||
onPressed: () async {
|
||||
await clipboardInterface
|
||||
.setData(ClipboardData(text: mnemonic.join(" ")));
|
||||
if (context.mounted) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
|
@ -152,19 +126,22 @@ class WalletBackupView extends ConsumerWidget {
|
|||
}
|
||||
|
||||
class _Mnemonic extends ConsumerWidget {
|
||||
const _Mnemonic({super.key, required this.walletId, required this.mnemonic});
|
||||
const _Mnemonic({
|
||||
super.key,
|
||||
required this.walletId,
|
||||
required this.mnemonic,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
});
|
||||
|
||||
final String walletId;
|
||||
final List<String> mnemonic;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
ref.watch(pWalletName(walletId)),
|
||||
textAlign: TextAlign.center,
|
||||
|
@ -212,10 +189,28 @@ class _Mnemonic extends ConsumerWidget {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonStyle(context),
|
||||
SecondaryButton(
|
||||
label: "Copy",
|
||||
onPressed: () async {
|
||||
await clipboardInterface
|
||||
.setData(ClipboardData(text: mnemonic.join(" ")));
|
||||
if (context.mounted) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
PrimaryButton(
|
||||
label: "Show QR Code",
|
||||
onPressed: () {
|
||||
final String data = AddressUtils.encodeQRSeedData(mnemonic);
|
||||
|
||||
|
@ -284,10 +279,6 @@ class _Mnemonic extends ConsumerWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
"Show QR Code",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -301,8 +292,6 @@ class _FrostKeys extends StatelessWidget {
|
|||
this.frostWalletData,
|
||||
});
|
||||
|
||||
static const String routeName = "/walletBackup";
|
||||
|
||||
final String walletId;
|
||||
final ({
|
||||
String myName,
|
||||
|
@ -433,19 +422,19 @@ class _FrostKeys extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class MobileXPrivsView extends StatelessWidget {
|
||||
const MobileXPrivsView({
|
||||
class MobileKeyDataView extends StatelessWidget {
|
||||
const MobileKeyDataView({
|
||||
super.key,
|
||||
required this.walletId,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
required this.xprivData,
|
||||
required this.keyData,
|
||||
});
|
||||
|
||||
static const String routeName = "/mobileXPrivView";
|
||||
|
||||
final String walletId;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
final ({List<XPriv> xprivs, String fingerprint}) xprivData;
|
||||
final KeyDataInterface keyData;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -459,7 +448,13 @@ class MobileXPrivsView extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
title: Text(
|
||||
"Wallet xpriv(s)",
|
||||
"Wallet ${switch (keyData.runtimeType) {
|
||||
const (XPrivData) => "xpriv(s)",
|
||||
const (CWKeyData) => "keys",
|
||||
_ => throw UnimplementedError(
|
||||
"Don't forget to add your KeyDataInterface here!",
|
||||
),
|
||||
}}",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
),
|
||||
|
@ -475,10 +470,19 @@ class MobileXPrivsView extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: WalletXPrivs(
|
||||
walletId: walletId,
|
||||
xprivData: xprivData,
|
||||
),
|
||||
child: switch (keyData.runtimeType) {
|
||||
const (XPrivData) => WalletXPrivs(
|
||||
walletId: walletId,
|
||||
xprivData: keyData as XPrivData,
|
||||
),
|
||||
const (CWKeyData) => CNWalletKeys(
|
||||
walletId: walletId,
|
||||
cwKeyData: keyData as CWKeyData,
|
||||
),
|
||||
_ => throw UnimplementedError(
|
||||
"Don't forget to add your KeyDataInterface here!",
|
||||
),
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../../../models/keys/xpriv_data.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
|
@ -23,7 +24,6 @@ import '../../../../utilities/clipboard_interface.dart';
|
|||
import '../../../../utilities/constants.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import '../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../widgets/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
|
@ -37,7 +37,7 @@ class WalletXPrivs extends ConsumerStatefulWidget {
|
|||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
});
|
||||
|
||||
final ({List<XPriv> xprivs, String fingerprint}) xprivData;
|
||||
final XPrivData xprivData;
|
||||
final String walletId;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:tuple/tuple.dart';
|
|||
import '../../../db/hive/db.dart';
|
||||
import '../../../db/sqlite/firo_cache.dart';
|
||||
import '../../../models/epicbox_config_model.dart';
|
||||
import '../../../models/keys/key_data_interface.dart';
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import '../../../providers/global/wallets_provider.dart';
|
||||
import '../../../providers/ui/transaction_filter_provider.dart';
|
||||
|
@ -35,6 +36,7 @@ import '../../../wallets/crypto_currency/intermediate/frost_currency.dart';
|
|||
import '../../../wallets/crypto_currency/intermediate/nano_currency.dart';
|
||||
import '../../../wallets/wallet/impl/bitcoin_frost_wallet.dart';
|
||||
import '../../../wallets/wallet/impl/epiccash_wallet.dart';
|
||||
import '../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||
import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import '../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||
import '../../../widgets/background.dart';
|
||||
|
@ -261,10 +263,6 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
|
|||
|
||||
// TODO: [prio=med] take wallets that don't have a mnemonic into account
|
||||
|
||||
({
|
||||
List<XPriv> xprivs,
|
||||
String fingerprint
|
||||
})? xprivData;
|
||||
List<String>? mnemonic;
|
||||
({
|
||||
String myName,
|
||||
|
@ -306,8 +304,11 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
|
|||
await wallet.getMnemonicAsWords();
|
||||
}
|
||||
|
||||
KeyDataInterface? keyData;
|
||||
if (wallet is ExtendedKeysInterface) {
|
||||
xprivData = await wallet.getXPrivs();
|
||||
keyData = await wallet.getXPrivs();
|
||||
} else if (wallet is CwBasedInterface) {
|
||||
keyData = await wallet.getKeys();
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
|
@ -323,7 +324,7 @@ class _WalletSettingsViewState extends ConsumerState<WalletSettingsView> {
|
|||
mnemonic: mnemonic ?? [],
|
||||
frostWalletData:
|
||||
frostWalletData,
|
||||
xprivData: xprivData,
|
||||
keyData: keyData,
|
||||
),
|
||||
showBackButton: true,
|
||||
routeOnSuccess:
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../models/keys/key_data_interface.dart';
|
||||
import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
|
||||
import '../../pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
|
||||
import '../../providers/global/wallets_provider.dart';
|
||||
|
@ -11,6 +12,7 @@ import '../../utilities/assets.dart';
|
|||
import '../../utilities/text_styles.dart';
|
||||
import '../../utilities/util.dart';
|
||||
import '../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||
import '../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import '../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||
import '../../widgets/background.dart';
|
||||
|
@ -265,9 +267,11 @@ class _FiroRescanRecoveryErrorViewState
|
|||
if (wallet is MnemonicInterface) {
|
||||
final mnemonic = await wallet.getMnemonicAsWords();
|
||||
|
||||
({List<XPriv> xprivs, String fingerprint})? xprivData;
|
||||
KeyDataInterface? keyData;
|
||||
if (wallet is ExtendedKeysInterface) {
|
||||
xprivData = await wallet.getXPrivs();
|
||||
keyData = await wallet.getXPrivs();
|
||||
} else if (wallet is CwBasedInterface) {
|
||||
keyData = await wallet.getKeys();
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
|
@ -280,7 +284,7 @@ class _FiroRescanRecoveryErrorViewState
|
|||
routeOnSuccessArguments: (
|
||||
walletId: widget.walletId,
|
||||
mnemonic: mnemonic,
|
||||
xprivData: xprivData,
|
||||
keyData: keyData,
|
||||
),
|
||||
showBackButton: true,
|
||||
routeOnSuccess: WalletBackupView.routeName,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
|
||||
import '../../../providers/cash_fusion/fusion_progress_ui_state_provider.dart';
|
||||
import '../../../providers/global/prefs_provider.dart';
|
||||
|
@ -137,6 +139,8 @@ class _FusionDialogViewState extends ConsumerState<FusionDialogView> {
|
|||
message: "Stopping fusion",
|
||||
);
|
||||
|
||||
await Wakelock.disable();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -150,6 +154,12 @@ class _FusionDialogViewState extends ConsumerState<FusionDialogView> {
|
|||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
Wakelock.disable();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool _succeeded =
|
||||
|
@ -162,6 +172,10 @@ class _FusionDialogViewState extends ConsumerState<FusionDialogView> {
|
|||
.watch(fusionProgressUIStateProvider(widget.walletId))
|
||||
.fusionRoundsCompleted;
|
||||
|
||||
if (!Platform.isLinux) {
|
||||
Wakelock.enable();
|
||||
}
|
||||
|
||||
return DesktopDialog(
|
||||
maxHeight: 600,
|
||||
child: SingleChildScrollView(
|
||||
|
|
|
@ -338,7 +338,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
onTap: () {
|
||||
clipboard.setData(
|
||||
ClipboardData(
|
||||
text: ref.watch(pWalletReceivingAddress(walletId)),
|
||||
text: address,
|
||||
),
|
||||
);
|
||||
showFloatingFlushBar(
|
||||
|
|
|
@ -19,6 +19,7 @@ import '../../../../../providers/global/wallets_provider.dart';
|
|||
import '../../../../../themes/stack_colors.dart';
|
||||
import '../../../../../utilities/assets.dart';
|
||||
import '../../../../../utilities/text_styles.dart';
|
||||
import '../../../../../utilities/util.dart';
|
||||
import '../../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../../../wallets/isar/models/wallet_info.dart';
|
||||
import '../../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
|
@ -32,7 +33,10 @@ import '../../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.da
|
|||
import '../../../../../widgets/custom_buttons/draggable_switch_button.dart';
|
||||
import '../../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import '../../../../../widgets/desktop/primary_button.dart';
|
||||
import '../../../../../widgets/desktop/secondary_button.dart';
|
||||
import '../../../../../widgets/rounded_container.dart';
|
||||
import '../../../../../widgets/stack_dialog.dart';
|
||||
|
||||
class MoreFeaturesDialog extends ConsumerStatefulWidget {
|
||||
const MoreFeaturesDialog({
|
||||
|
@ -102,6 +106,147 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
|
|||
}
|
||||
}
|
||||
|
||||
bool _switchReuseAddressToggledLock = false; // Mutex.
|
||||
Future<void> _switchReuseAddressToggled(bool newValue) async {
|
||||
if (newValue) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
return isDesktop
|
||||
? DesktopDialog(
|
||||
maxWidth: 576,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 32),
|
||||
child: Text(
|
||||
"Warning!",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 32,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 43,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
label: "Cancel",
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
label: "Continue",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: StackDialog(
|
||||
title: "Warning!",
|
||||
message:
|
||||
"Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?",
|
||||
leftButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getSecondaryEnabledButtonStyle(context),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
),
|
||||
rightButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonStyle(context),
|
||||
child: Text(
|
||||
"Continue",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
).then((confirmed) async {
|
||||
if (_switchReuseAddressToggledLock) {
|
||||
return;
|
||||
}
|
||||
_switchReuseAddressToggledLock = true; // Lock mutex.
|
||||
|
||||
try {
|
||||
if (confirmed == true) {
|
||||
await ref.read(pWalletInfo(widget.walletId)).updateOtherData(
|
||||
newEntries: {
|
||||
WalletInfoKeys.reuseAddress: true,
|
||||
},
|
||||
isar: ref.read(mainDBProvider).isar,
|
||||
);
|
||||
} else {
|
||||
await ref.read(pWalletInfo(widget.walletId)).updateOtherData(
|
||||
newEntries: {
|
||||
WalletInfoKeys.reuseAddress: false,
|
||||
},
|
||||
isar: ref.read(mainDBProvider).isar,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
// ensure _switchReuseAddressToggledLock is set to false no matter what.
|
||||
_switchReuseAddressToggledLock = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await ref.read(pWalletInfo(widget.walletId)).updateOtherData(
|
||||
newEntries: {
|
||||
WalletInfoKeys.reuseAddress: false,
|
||||
},
|
||||
isar: ref.read(mainDBProvider).isar,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final wallet = ref.watch(
|
||||
|
@ -253,6 +398,38 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
|
|||
],
|
||||
),
|
||||
),
|
||||
// reuseAddress preference.
|
||||
_MoreFeaturesItemBase(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 3),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 40,
|
||||
child: DraggableSwitchButton(
|
||||
isOn: ref.watch(
|
||||
pWalletInfo(widget.walletId)
|
||||
.select((value) => value.otherData),
|
||||
)[WalletInfoKeys.reuseAddress] as bool? ??
|
||||
false,
|
||||
onValueChanged: _switchReuseAddressToggled,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Reuse receiving address by default",
|
||||
style: STextStyles.w600_20(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 28,
|
||||
),
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../../../models/keys/key_data_interface.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/desktop/storage_crypto_handler_provider.dart';
|
||||
import '../../../../providers/providers.dart';
|
||||
|
@ -22,6 +23,7 @@ import '../../../../utilities/assets.dart';
|
|||
import '../../../../utilities/constants.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../wallets/wallet/impl/bitcoin_frost_wallet.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/mnemonic_interface.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
|
@ -85,7 +87,6 @@ class _UnlockWalletKeysDesktopState
|
|||
final wallet = ref.read(pWallets).getWallet(widget.walletId);
|
||||
({String keys, String config})? frostData;
|
||||
List<String>? words;
|
||||
({List<XPriv> xprivs, String fingerprint})? xprivData;
|
||||
|
||||
// TODO: [prio=low] handle wallets that don't have a mnemonic
|
||||
// All wallets currently are mnemonic based
|
||||
|
@ -102,8 +103,11 @@ class _UnlockWalletKeysDesktopState
|
|||
words = await wallet.getMnemonicAsWords();
|
||||
}
|
||||
|
||||
KeyDataInterface? keyData;
|
||||
if (wallet is ExtendedKeysInterface) {
|
||||
xprivData = await wallet.getXPrivs();
|
||||
keyData = await wallet.getXPrivs();
|
||||
} else if (wallet is CwBasedInterface) {
|
||||
keyData = await wallet.getKeys();
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
|
@ -113,7 +117,7 @@ class _UnlockWalletKeysDesktopState
|
|||
mnemonic: words ?? [],
|
||||
walletId: widget.walletId,
|
||||
frostData: frostData,
|
||||
xprivData: xprivData,
|
||||
keyData: keyData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -327,10 +331,6 @@ class _UnlockWalletKeysDesktopState
|
|||
|
||||
({String keys, String config})? frostData;
|
||||
List<String>? words;
|
||||
({
|
||||
List<XPriv> xprivs,
|
||||
String fingerprint
|
||||
})? xprivData;
|
||||
|
||||
final wallet =
|
||||
ref.read(pWallets).getWallet(widget.walletId);
|
||||
|
@ -350,11 +350,14 @@ class _UnlockWalletKeysDesktopState
|
|||
words = await wallet.getMnemonicAsWords();
|
||||
}
|
||||
|
||||
KeyDataInterface? keyData;
|
||||
if (wallet is ExtendedKeysInterface) {
|
||||
xprivData = await wallet.getXPrivs();
|
||||
keyData = await wallet.getXPrivs();
|
||||
} else if (wallet is CwBasedInterface) {
|
||||
keyData = await wallet.getKeys();
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
if (context.mounted) {
|
||||
await Navigator.of(context)
|
||||
.pushReplacementNamed(
|
||||
WalletKeysDesktopPopup.routeName,
|
||||
|
@ -362,7 +365,7 @@ class _UnlockWalletKeysDesktopState
|
|||
mnemonic: words ?? [],
|
||||
walletId: widget.walletId,
|
||||
frostData: frostData,
|
||||
xprivData: xprivData,
|
||||
keyData: keyData,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,8 +14,12 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../models/keys/cw_key_data.dart';
|
||||
import '../../../../models/keys/key_data_interface.dart';
|
||||
import '../../../../models/keys/xpriv_data.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
|
||||
import '../../../../pages/settings_views/wallet_settings_view/wallet_backup_views/cn_wallet_keys.dart';
|
||||
import '../../../../pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_xprivs.dart';
|
||||
import '../../../../pages/wallet_view/transaction_views/transaction_details_view.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -23,7 +27,6 @@ import '../../../../utilities/address_utils.dart';
|
|||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/clipboard_interface.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
|
||||
import '../../../../widgets/custom_tab_view.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
|
@ -39,14 +42,14 @@ class WalletKeysDesktopPopup extends ConsumerWidget {
|
|||
required this.walletId,
|
||||
this.frostData,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
this.xprivData,
|
||||
this.keyData,
|
||||
});
|
||||
|
||||
final List<String> words;
|
||||
final String walletId;
|
||||
final ({String keys, String config})? frostData;
|
||||
final ClipboardInterface clipboardInterface;
|
||||
final ({List<XPriv> xprivs, String fingerprint})? xprivData;
|
||||
final KeyDataInterface? keyData;
|
||||
|
||||
static const String routeName = "walletKeysDesktopPopup";
|
||||
|
||||
|
@ -176,9 +179,13 @@ class WalletKeysDesktopPopup extends ConsumerWidget {
|
|||
),
|
||||
],
|
||||
)
|
||||
: xprivData != null
|
||||
: keyData != null
|
||||
? CustomTabView(
|
||||
titles: const ["Mnemonic", "XPriv(s)"],
|
||||
titles: [
|
||||
"Mnemonic",
|
||||
if (keyData is XPrivData) "XPriv(s)",
|
||||
if (keyData is CWKeyData) "Keys",
|
||||
],
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
|
@ -186,10 +193,16 @@ class WalletKeysDesktopPopup extends ConsumerWidget {
|
|||
words: words,
|
||||
),
|
||||
),
|
||||
WalletXPrivs(
|
||||
xprivData: xprivData!,
|
||||
walletId: walletId,
|
||||
),
|
||||
if (keyData is XPrivData)
|
||||
WalletXPrivs(
|
||||
xprivData: keyData as XPrivData,
|
||||
walletId: walletId,
|
||||
),
|
||||
if (keyData is CWKeyData)
|
||||
CNWalletKeys(
|
||||
cwKeyData: keyData as CWKeyData,
|
||||
walletId: walletId,
|
||||
),
|
||||
],
|
||||
)
|
||||
: _Mnemonic(
|
||||
|
@ -264,22 +277,6 @@ class _Mnemonic extends StatelessWidget {
|
|||
children: [
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Show QR code",
|
||||
onPressed: () {
|
||||
// TODO: address utils
|
||||
final String value = AddressUtils.encodeQRSeedData(words);
|
||||
Navigator.of(context).pushNamed(
|
||||
QRCodeDesktopPopupContent.routeName,
|
||||
arguments: value,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Copy",
|
||||
onPressed: () async {
|
||||
await clipboardInterface.setData(
|
||||
|
@ -298,6 +295,22 @@ class _Mnemonic extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: "Show QR code",
|
||||
onPressed: () {
|
||||
// TODO: address utils
|
||||
final String value = AddressUtils.encodeQRSeedData(words);
|
||||
Navigator.of(context).pushNamed(
|
||||
QRCodeDesktopPopupContent.routeName,
|
||||
arguments: value,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -22,6 +22,7 @@ import 'models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
|||
import 'models/isar/models/contact_entry.dart';
|
||||
import 'models/isar/models/isar_models.dart';
|
||||
import 'models/isar/ordinal.dart';
|
||||
import 'models/keys/key_data_interface.dart';
|
||||
import 'models/paynym/paynym_account_lite.dart';
|
||||
import 'models/send_view_auto_fill_data.dart';
|
||||
import 'pages/add_wallet_views/add_token_view/add_custom_token_view.dart';
|
||||
|
@ -1280,14 +1281,14 @@ class RouteGenerator {
|
|||
} else if (args is ({
|
||||
String walletId,
|
||||
List<String> mnemonic,
|
||||
({List<XPriv> xprivs, String fingerprint})? xprivData,
|
||||
KeyDataInterface? keyData,
|
||||
})) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => WalletBackupView(
|
||||
walletId: args.walletId,
|
||||
mnemonic: args.mnemonic,
|
||||
xprivData: args.xprivData,
|
||||
keyData: args.keyData,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
|
@ -1296,7 +1297,7 @@ class RouteGenerator {
|
|||
} else if (args is ({
|
||||
String walletId,
|
||||
List<String> mnemonic,
|
||||
({List<XPriv> xprivs, String fingerprint})? xprivData,
|
||||
KeyDataInterface? keyData,
|
||||
({
|
||||
String myName,
|
||||
String config,
|
||||
|
@ -1310,7 +1311,7 @@ class RouteGenerator {
|
|||
walletId: args.walletId,
|
||||
mnemonic: args.mnemonic,
|
||||
frostWalletData: args.frostWalletData,
|
||||
xprivData: args.xprivData,
|
||||
keyData: args.keyData,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
|
@ -1319,16 +1320,16 @@ class RouteGenerator {
|
|||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case MobileXPrivsView.routeName:
|
||||
case MobileKeyDataView.routeName:
|
||||
if (args is ({
|
||||
String walletId,
|
||||
({List<XPriv> xprivs, String fingerprint}) xprivData,
|
||||
KeyDataInterface keyData,
|
||||
})) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => MobileXPrivsView(
|
||||
builder: (_) => MobileKeyDataView(
|
||||
walletId: args.walletId,
|
||||
xprivData: args.xprivData,
|
||||
keyData: args.keyData,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
|
@ -2369,14 +2370,14 @@ class RouteGenerator {
|
|||
List<String> mnemonic,
|
||||
String walletId,
|
||||
({String keys, String config})? frostData,
|
||||
({List<XPriv> xprivs, String fingerprint})? xprivData,
|
||||
KeyDataInterface? keyData,
|
||||
})) {
|
||||
return FadePageRoute(
|
||||
WalletKeysDesktopPopup(
|
||||
words: args.mnemonic,
|
||||
walletId: args.walletId,
|
||||
frostData: args.frostData,
|
||||
xprivData: args.xprivData,
|
||||
keyData: args.keyData,
|
||||
),
|
||||
RouteSettings(
|
||||
name: settings.name,
|
||||
|
@ -2385,13 +2386,13 @@ class RouteGenerator {
|
|||
} else if (args is ({
|
||||
List<String> mnemonic,
|
||||
String walletId,
|
||||
({List<XPriv> xprivs, String fingerprint})? xprivData,
|
||||
KeyDataInterface? keyData,
|
||||
})) {
|
||||
return FadePageRoute(
|
||||
WalletKeysDesktopPopup(
|
||||
words: args.mnemonic,
|
||||
walletId: args.walletId,
|
||||
xprivData: args.xprivData,
|
||||
keyData: args.keyData,
|
||||
),
|
||||
RouteSettings(
|
||||
name: settings.name,
|
||||
|
|
|
@ -511,4 +511,5 @@ abstract class WalletInfoKeys {
|
|||
static const String firoSparkCacheSetTimestampCache =
|
||||
"firoSparkCacheSetTimestampCacheKey";
|
||||
static const String enableOptInRbf = "enableOptInRbfKey";
|
||||
static const String reuseAddress = "reuseAddressKey";
|
||||
}
|
||||
|
|
|
@ -133,21 +133,41 @@ class TxData {
|
|||
.reduce((total, amount) => total += amount)
|
||||
: null;
|
||||
|
||||
Amount? get amountWithoutChange =>
|
||||
recipients != null && recipients!.isNotEmpty
|
||||
? recipients!
|
||||
.where((e) => !e.isChange)
|
||||
.map((e) => e.amount)
|
||||
.reduce((total, amount) => total += amount)
|
||||
: null;
|
||||
Amount? get amountWithoutChange {
|
||||
if (recipients != null && recipients!.isNotEmpty) {
|
||||
if (recipients!.where((e) => !e.isChange).isEmpty) {
|
||||
return Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: recipients!.first.amount.fractionDigits,
|
||||
);
|
||||
} else {
|
||||
return recipients!
|
||||
.where((e) => !e.isChange)
|
||||
.map((e) => e.amount)
|
||||
.reduce((total, amount) => total += amount);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Amount? get amountSparkWithoutChange =>
|
||||
sparkRecipients != null && sparkRecipients!.isNotEmpty
|
||||
? sparkRecipients!
|
||||
.where((e) => !e.isChange)
|
||||
.map((e) => e.amount)
|
||||
.reduce((total, amount) => total += amount)
|
||||
: null;
|
||||
Amount? get amountSparkWithoutChange {
|
||||
if (sparkRecipients != null && sparkRecipients!.isNotEmpty) {
|
||||
if (sparkRecipients!.where((e) => !e.isChange).isEmpty) {
|
||||
return Amount(
|
||||
rawValue: BigInt.zero,
|
||||
fractionDigits: sparkRecipients!.first.amount.fractionDigits,
|
||||
);
|
||||
} else {
|
||||
return sparkRecipients!
|
||||
.where((e) => !e.isChange)
|
||||
.map((e) => e.amount)
|
||||
.reduce((total, amount) => total += amount);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
int? get estimatedSatsPerVByte => fee != null && vSize != null
|
||||
? (fee!.raw ~/ BigInt.from(vSize!)).toInt()
|
||||
|
|
|
@ -787,7 +787,9 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
|
|||
|
||||
for (final tuple in receiveResults) {
|
||||
if (tuple.addresses.isEmpty) {
|
||||
await checkReceivingAddressForTransactions();
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
|
||||
await checkReceivingAddressForTransactions();
|
||||
}
|
||||
} else {
|
||||
highestReceivingIndexWithHistory = max(
|
||||
tuple.index,
|
||||
|
|
|
@ -25,6 +25,7 @@ import 'package:tuple/tuple.dart';
|
|||
import '../../../db/hive/db.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart';
|
||||
import '../../../models/keys/cw_key_data.dart';
|
||||
import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart';
|
||||
import '../../../services/event_bus/events/global/tor_status_changed_event.dart';
|
||||
import '../../../services/event_bus/global_event_bus.dart';
|
||||
|
@ -235,6 +236,25 @@ class MoneroWallet extends CryptonoteWallet with CwBasedInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CWKeyData?> getKeys() async {
|
||||
final base = (CwBasedInterface.cwWalletBase as MoneroWalletBase?);
|
||||
|
||||
if (base == null ||
|
||||
base.walletInfo.name != walletId ||
|
||||
CwBasedInterface.exitMutex.isLocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CWKeyData(
|
||||
walletId: walletId,
|
||||
publicViewKey: base.keys.publicViewKey,
|
||||
privateViewKey: base.keys.privateViewKey,
|
||||
publicSpendKey: base.keys.publicSpendKey,
|
||||
privateSpendKey: base.keys.privateSpendKey,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
final base = (CwBasedInterface.cwWalletBase as MoneroWalletBase?);
|
||||
|
|
|
@ -28,6 +28,7 @@ import 'package:tuple/tuple.dart';
|
|||
import '../../../db/hive/db.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart';
|
||||
import '../../../models/keys/cw_key_data.dart';
|
||||
import '../../../services/event_bus/events/global/tor_connection_status_changed_event.dart';
|
||||
import '../../../services/event_bus/events/global/tor_status_changed_event.dart';
|
||||
import '../../../services/event_bus/global_event_bus.dart';
|
||||
|
@ -214,6 +215,25 @@ class WowneroWallet extends CryptonoteWallet with CwBasedInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<CWKeyData?> getKeys() async {
|
||||
final base = (CwBasedInterface.cwWalletBase as WowneroWalletBase?);
|
||||
|
||||
if (base == null ||
|
||||
base.walletInfo.name != walletId ||
|
||||
CwBasedInterface.exitMutex.isLocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CWKeyData(
|
||||
walletId: walletId,
|
||||
publicViewKey: base.keys.publicViewKey,
|
||||
privateViewKey: base.keys.privateViewKey,
|
||||
publicSpendKey: base.keys.publicSpendKey,
|
||||
privateSpendKey: base.keys.privateSpendKey,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateTransactions() async {
|
||||
final base = (CwBasedInterface.cwWalletBase as WowneroWalletBase?);
|
||||
|
|
|
@ -522,8 +522,10 @@ abstract class Wallet<T extends CryptoCurrency> {
|
|||
|
||||
// TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided.
|
||||
if (this is MultiAddressInterface) {
|
||||
await (this as MultiAddressInterface)
|
||||
.checkReceivingAddressForTransactions();
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
|
||||
await (this as MultiAddressInterface)
|
||||
.checkReceivingAddressForTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId));
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:mutex/mutex.dart';
|
|||
|
||||
import '../../../models/balance.dart';
|
||||
import '../../../models/isar/models/blockchain_data/address.dart';
|
||||
import '../../../models/keys/cw_key_data.dart';
|
||||
import '../../../models/paymint/fee_object_model.dart';
|
||||
import '../../../services/event_bus/events/global/blocks_remaining_event.dart';
|
||||
import '../../../services/event_bus/events/global/refresh_percent_changed_event.dart';
|
||||
|
@ -24,6 +25,7 @@ import '../../../utilities/amount/amount.dart';
|
|||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/stack_file_system.dart';
|
||||
import '../../crypto_currency/intermediate/cryptonote_currency.dart';
|
||||
import '../../isar/models/wallet_info.dart';
|
||||
import '../intermediate/cryptonote_wallet.dart';
|
||||
import 'multi_address_interface.dart';
|
||||
|
||||
|
@ -195,6 +197,8 @@ mixin CwBasedInterface<T extends CryptonoteCurrency> on CryptonoteWallet<T>
|
|||
|
||||
Address addressFor({required int index, int account = 0});
|
||||
|
||||
Future<CWKeyData?> getKeys();
|
||||
|
||||
// ============ Private ======================================================
|
||||
Future<void> _refreshTxDataHelper() async {
|
||||
if (_txRefreshLock) return;
|
||||
|
@ -283,7 +287,9 @@ mixin CwBasedInterface<T extends CryptonoteCurrency> on CryptonoteWallet<T>
|
|||
await updateTransactions();
|
||||
await updateBalance();
|
||||
|
||||
await checkReceivingAddressForTransactions();
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
|
||||
await checkReceivingAddressForTransactions();
|
||||
}
|
||||
|
||||
if (cwWalletBase?.syncStatus is SyncedSyncStatus) {
|
||||
refreshMutex.release();
|
||||
|
@ -339,6 +345,17 @@ mixin CwBasedInterface<T extends CryptonoteCurrency> on CryptonoteWallet<T>
|
|||
|
||||
@override
|
||||
Future<void> checkReceivingAddressForTransactions() async {
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] == true) {
|
||||
try {
|
||||
throw Exception();
|
||||
} catch (_, s) {
|
||||
Logging.instance.log(
|
||||
"checkReceivingAddressForTransactions called but reuse address flag set: $s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
int highestIndex = -1;
|
||||
final entries = cwWalletBase?.transactionHistory?.transactions?.entries;
|
||||
|
@ -377,8 +394,10 @@ mixin CwBasedInterface<T extends CryptonoteCurrency> on CryptonoteWallet<T>
|
|||
// we need to update the address
|
||||
await mainDB.updateAddress(existing, newReceivingAddress);
|
||||
}
|
||||
// keep checking until address with no tx history is set as current
|
||||
await checkReceivingAddressForTransactions();
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
|
||||
// keep checking until address with no tx history is set as current
|
||||
await checkReceivingAddressForTransactions();
|
||||
}
|
||||
}
|
||||
} on SocketException catch (se, s) {
|
||||
Logging.instance.log(
|
||||
|
|
|
@ -23,6 +23,7 @@ import '../../../utilities/logger.dart';
|
|||
import '../../../utilities/paynym_is_api.dart';
|
||||
import '../../crypto_currency/coins/firo.dart';
|
||||
import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
|
||||
import '../../isar/models/wallet_info.dart';
|
||||
import '../../models/tx_data.dart';
|
||||
import '../impl/bitcoin_wallet.dart';
|
||||
import '../impl/firo_wallet.dart';
|
||||
|
@ -1315,6 +1316,17 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
|||
|
||||
@override
|
||||
Future<void> checkReceivingAddressForTransactions() async {
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] == true) {
|
||||
try {
|
||||
throw Exception();
|
||||
} catch (_, s) {
|
||||
Logging.instance.log(
|
||||
"checkReceivingAddressForTransactions called but reuse address flag set: $s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final currentReceiving = await getCurrentReceivingAddress();
|
||||
|
||||
|
@ -1336,9 +1348,12 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
|
|||
if (needsGenerate) {
|
||||
await generateNewReceivingAddress();
|
||||
|
||||
// TODO: get rid of this? Could cause problems (long loading/infinite loop or something)
|
||||
// keep checking until address with no tx history is set as current
|
||||
await checkReceivingAddressForTransactions();
|
||||
// TODO: [prio=low] Make sure we scan all addresses but only show one.
|
||||
if (info.otherData[WalletInfoKeys.reuseAddress] != true) {
|
||||
// TODO: get rid of this? Could cause problems (long loading/infinite loop or something)
|
||||
// keep checking until address with no tx history is set as current
|
||||
await checkReceivingAddressForTransactions();
|
||||
}
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import '../../../models/keys/xpriv_data.dart';
|
||||
import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
|
||||
import 'electrumx_interface.dart';
|
||||
|
||||
|
@ -42,7 +43,7 @@ mixin ExtendedKeysInterface<T extends ElectrumXCurrencyInterface>
|
|||
);
|
||||
}
|
||||
|
||||
Future<({List<XPriv> xprivs, String fingerprint})> getXPrivs() async {
|
||||
Future<XPrivData> getXPrivs() async {
|
||||
final paths = cryptoCurrency.supportedDerivationPathTypes.map(
|
||||
(e) => (
|
||||
path: e,
|
||||
|
@ -71,7 +72,8 @@ mixin ExtendedKeysInterface<T extends ElectrumXCurrencyInterface>
|
|||
);
|
||||
});
|
||||
|
||||
return (
|
||||
return XPrivData(
|
||||
walletId: walletId,
|
||||
fingerprint: fingerprint,
|
||||
xprivs: [
|
||||
(
|
||||
|
|
|
@ -171,7 +171,12 @@ dependencies:
|
|||
convert: ^3.1.1
|
||||
flutter_hooks: ^0.20.3
|
||||
meta: ^1.9.1
|
||||
coinlib_flutter: ^2.0.0
|
||||
# coinlib_flutter: ^2.1.0-rc.1
|
||||
coinlib_flutter:
|
||||
git:
|
||||
url: https://github.com/peercoin/coinlib.git
|
||||
ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f
|
||||
path: coinlib_flutter
|
||||
electrum_adapter:
|
||||
git:
|
||||
url: https://github.com/cypherstack/electrum_adapter.git
|
||||
|
@ -180,7 +185,7 @@ dependencies:
|
|||
solana:
|
||||
git: # TODO [prio=low]: Revert to official package once Tor support is merged upstream.
|
||||
url: https://github.com/cypherstack/espresso-cash-public.git
|
||||
ref: a83e375678eb22fe544dc125d29bbec0fb833882
|
||||
ref: 706be5f166d31736c443cca4cd311b7fcfc2a9bf
|
||||
path: packages/solana
|
||||
calendar_date_picker2: ^1.0.2
|
||||
sqlite3: ^2.4.3
|
||||
|
@ -210,6 +215,20 @@ flutter_native_splash:
|
|||
android_disable_fullscreen: true
|
||||
|
||||
dependency_overrides:
|
||||
|
||||
# coin lib git for testing while waiting for publishing
|
||||
coinlib:
|
||||
git:
|
||||
url: https://github.com/peercoin/coinlib.git
|
||||
ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f
|
||||
path: coinlib
|
||||
|
||||
coinlib_flutter:
|
||||
git:
|
||||
url: https://github.com/peercoin/coinlib.git
|
||||
ref: b88e68e2e10ffe54d802deeed6b9e83da7721a1f
|
||||
path: coinlib_flutter
|
||||
|
||||
# adding here due to pure laziness
|
||||
tor_ffi_plugin:
|
||||
git:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
$KEYS = "..\lib\external_api_keys.dart"
|
||||
if (-not (Test-Path $KEYS)) {
|
||||
Write-Host "prebuild.ps1: creating template lib/external_api_keys.dart file"
|
||||
"const kChangeNowApiKey = '';" + "`nconst kSimpleSwapApiKey = '';" | Out-File $KEYS -Encoding UTF8
|
||||
"const kChangeNowApiKey = '';" + "`nconst kSimpleSwapApiKey = '';" + "`nconst kNanswapApiKey = '';" + "`nconst kNanoSwapRpcApiKey = '';" | Out-File $KEYS -Encoding UTF8
|
||||
}
|
||||
|
||||
# Create template wallet test parameter files if they don't already exist
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
KEYS=../lib/external_api_keys.dart
|
||||
if ! test -f "$KEYS"; then
|
||||
echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
|
||||
printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";\n' > $KEYS
|
||||
printf 'const kChangeNowApiKey = "";\nconst kSimpleSwapApiKey = "";\nconst kNanswapApiKey = "";\nconst kNanoSwapRpcApiKey = "";\n' > $KEYS
|
||||
fi
|
||||
|
||||
# Create template wallet test parameter files if they don't already exist
|
||||
|
|
Loading…
Reference in a new issue