mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-24 08:09:03 +00:00
Merge branch 'staging' into togglebalance
This commit is contained in:
commit
e91daa943a
132 changed files with 4928 additions and 1216 deletions
asset_sources
default_themes/campfire
other/ios_launch_image
crypto_plugins
ios
lib
app_config.dartmain.dartdesktop_menu.dartdesktop_menu_item.dartroute_generator.dart
models
isar/models
keys
pages
add_wallet_views
frost_ms/new/steps
new_wallet_recovery_phrase_warning_view
verify_recovery_phrase_view
address_book_views/subviews
buy_view
cashfusion
exchange_view
home_view
intro_view.dartpinpad_views
receive_view/addresses
send_view
settings_views
global_settings_view
wallet_settings_view
special
stack_privacy_calls.darttoken_view/sub_widgets
wallet_view
transaction_views
wallet_view.dartwallets_view
pages_desktop_specific
cashfusion/sub_widgets
desktop_exchange
exchange_steps
subwidgets
my_stack_view
dialogs
wallet_view/sub_widgets
password
delete_password_warning_view.dartforgot_password_desktop_view.dartforgotten_passphrase_restore_from_swb.dart
settings/settings_menu
services
themes
utilities
wallets/crypto_currency
Binary file not shown.
BIN
asset_sources/other/ios_launch_image/campfire/LaunchImage.png
Normal file
BIN
asset_sources/other/ios_launch_image/campfire/LaunchImage.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 15 KiB |
BIN
asset_sources/other/ios_launch_image/campfire/LaunchImage@2x.png
Normal file
BIN
asset_sources/other/ios_launch_image/campfire/LaunchImage@2x.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 44 KiB |
BIN
asset_sources/other/ios_launch_image/campfire/LaunchImage@3x.png
Normal file
BIN
asset_sources/other/ios_launch_image/campfire/LaunchImage@3x.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 73 KiB |
Before ![]() (image error) Size: 2.2 KiB After ![]() (image error) Size: 2.2 KiB ![]() ![]() |
Before ![]() (image error) Size: 4.8 KiB After ![]() (image error) Size: 4.8 KiB ![]() ![]() |
Before ![]() (image error) Size: 16 KiB After ![]() (image error) Size: 16 KiB ![]() ![]() |
Binary file not shown.
After ![]() (image error) Size: 2.2 KiB |
Binary file not shown.
After ![]() (image error) Size: 4.8 KiB |
Binary file not shown.
After ![]() (image error) Size: 16 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit 19c76409e55f1bfed58855eb767574604376edb6
|
||||
Subproject commit 46a7da857d4113eb3998567b18ac0b33a470f4fd
|
|
@ -1 +1 @@
|
|||
Subproject commit d539de2348bdbb87bac341dcaa6a0755f21d48e2
|
||||
Subproject commit 2a74a97fb0f0e22a5280b22c010b710cdeec33bb
|
3
ios/.gitignore
vendored
3
ios/.gitignore
vendored
|
@ -30,3 +30,6 @@ Runner/GeneratedPluginRegistrant.*
|
|||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
||||
|
||||
# app specific, handled by scripts
|
||||
Runner/Assets.xcassets/LaunchImage.imageset/*.png
|
||||
|
|
|
@ -15,6 +15,8 @@ abstract class AppConfig {
|
|||
static const prefix = _prefix;
|
||||
static const suffix = _suffix;
|
||||
|
||||
static const emptyWalletsMessage = _emptyWalletsMessage;
|
||||
|
||||
static String get appDefaultDataDirName => _appDataDirName;
|
||||
static String get shortDescriptionText => _shortDescriptionText;
|
||||
static String get commitHash => _commitHash;
|
||||
|
|
|
@ -242,7 +242,7 @@ void main(List<String> args) async {
|
|||
|
||||
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||
// overlays: [SystemUiOverlay.bottom]);
|
||||
await NotificationApi.init();
|
||||
unawaited(NotificationApi.init());
|
||||
|
||||
await loadCoinlibFuture;
|
||||
|
||||
|
@ -378,7 +378,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
// TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
|
||||
// unawaited(_nodeService.updateCommunityNodes());
|
||||
|
||||
if (AppConfig.hasFeature(AppFeature.swap)) {
|
||||
if (AppConfig.hasFeature(AppFeature.swap) &&
|
||||
ref.read(prefsChangeNotifierProvider).enableExchange) {
|
||||
await ExchangeDataLoadingService.instance.initDB();
|
||||
// run without awaiting
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls &&
|
||||
|
|
|
@ -87,7 +87,10 @@ class TransactionV2 {
|
|||
);
|
||||
}
|
||||
|
||||
@ignore
|
||||
int? get size => _getFromOtherData(key: TxV2OdKeys.size) as int?;
|
||||
|
||||
@ignore
|
||||
int? get vSize => _getFromOtherData(key: TxV2OdKeys.vSize) as int?;
|
||||
|
||||
bool get isEpiccashTransaction =>
|
||||
|
@ -192,6 +195,9 @@ class TransactionV2 {
|
|||
required int currentChainHeight,
|
||||
required int minConfirms,
|
||||
}) {
|
||||
String prettyConfirms() =>
|
||||
"(${getConfirmations(currentChainHeight)}/$minConfirms)";
|
||||
|
||||
if (subType == TransactionSubType.cashFusion ||
|
||||
subType == TransactionSubType.mint ||
|
||||
(subType == TransactionSubType.sparkMint &&
|
||||
|
@ -199,7 +205,7 @@ class TransactionV2 {
|
|||
if (isConfirmed(currentChainHeight, minConfirms)) {
|
||||
return "Anonymized";
|
||||
} else {
|
||||
return "Anonymizing";
|
||||
return "Anonymizing ${prettyConfirms()}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +225,7 @@ class TransactionV2 {
|
|||
} else if ((numberOfMessages ?? 0) > 1) {
|
||||
return "Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no)
|
||||
} else {
|
||||
return "Receiving";
|
||||
return "Receiving ${prettyConfirms()}";
|
||||
}
|
||||
}
|
||||
} else if (type == TransactionType.outgoing) {
|
||||
|
@ -231,7 +237,7 @@ class TransactionV2 {
|
|||
} else if ((numberOfMessages ?? 0) > 1) {
|
||||
return "Sending (waiting for confirmations)";
|
||||
} else {
|
||||
return "Sending";
|
||||
return "Sending ${prettyConfirms()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,16 +250,20 @@ class TransactionV2 {
|
|||
if (isConfirmed(currentChainHeight, minConfirms)) {
|
||||
return "Received";
|
||||
} else {
|
||||
return "Receiving";
|
||||
return "Receiving ${prettyConfirms()}";
|
||||
}
|
||||
} else if (type == TransactionType.outgoing) {
|
||||
if (isConfirmed(currentChainHeight, minConfirms)) {
|
||||
return "Sent";
|
||||
} else {
|
||||
return "Sending";
|
||||
return "Sending ${prettyConfirms()}";
|
||||
}
|
||||
} else if (type == TransactionType.sentToSelf) {
|
||||
return "Sent to self";
|
||||
if (isConfirmed(currentChainHeight, minConfirms)) {
|
||||
return "Sent to self";
|
||||
} else {
|
||||
return "Sent to self ${prettyConfirms()}";
|
||||
}
|
||||
} else {
|
||||
return type.name;
|
||||
}
|
||||
|
|
35
lib/models/isar/models/sent_to_address.dart
Normal file
35
lib/models/isar/models/sent_to_address.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* This file is part of Stack Wallet.
|
||||
*
|
||||
* Copyright (c) 2023 Cypher Stack
|
||||
* All Rights Reserved.
|
||||
* The code is distributed under GPLv3 license, see LICENSE file for details.
|
||||
* Generated by Cypher Stack on 2023-05-26
|
||||
*
|
||||
*/
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'sent_to_address.g.dart';
|
||||
|
||||
@Collection()
|
||||
class SentToAddress {
|
||||
SentToAddress({
|
||||
required this.walletId,
|
||||
required this.txid,
|
||||
required this.value,
|
||||
this.label = "",
|
||||
});
|
||||
|
||||
Id id = Isar.autoIncrement;
|
||||
|
||||
@Index()
|
||||
late final String walletId;
|
||||
|
||||
@Index(unique: true, composite: [CompositeIndex("walletId")])
|
||||
late final String txid;
|
||||
|
||||
late final String value;
|
||||
|
||||
late final String label;
|
||||
}
|
1248
lib/models/isar/models/sent_to_address.g.dart
Normal file
1248
lib/models/isar/models/sent_to_address.g.dart
Normal file
File diff suppressed because it is too large
Load diff
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;
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'dart:typed_data';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../../app_config.dart';
|
||||
import '../../../../../frost_route_generator.dart';
|
||||
import '../../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../../pages_desktop_specific/desktop_home_view.dart';
|
||||
|
@ -45,7 +46,7 @@ class _FrostCreateStep5State extends ConsumerState<FrostCreateStep5> {
|
|||
static const _warning = "These are your private keys. Please back them up, "
|
||||
"keep them safe and never share it with anyone. Your private keys are the"
|
||||
" only way you can access your funds if you forget PIN, lose your phone, "
|
||||
"etc. Stack Wallet does not keep nor is able to restore your private keys"
|
||||
"etc. ${AppConfig.prefix} does not keep nor is able to restore your private keys"
|
||||
".";
|
||||
|
||||
late final String seed, recoveryString, serializedKeys, multisigConfig;
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../app_config.dart';
|
||||
import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import '../../../providers/db/main_db_provider.dart';
|
||||
import '../../../providers/global/secure_store_provider.dart';
|
||||
|
@ -173,7 +174,7 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
"write it down. Keep it safe and never share it with "
|
||||
"anyone. Your recovery phrase is the only way you can"
|
||||
" access your funds if you forget your PIN, lose your"
|
||||
" phone, etc.\n\nStack Wallet does not keep nor is "
|
||||
" phone, etc.\n\n${AppConfig.appName} does not keep nor is "
|
||||
"able to restore your recover phrase. Only you have "
|
||||
"access to your wallet.",
|
||||
style: isDesktop
|
||||
|
@ -427,7 +428,7 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
"I understand that Stack Wallet does not keep and cannot restore my recovery phrase, and If I lose my recovery phrase, I will not be able to access my funds.",
|
||||
"I understand that ${AppConfig.appName} does not keep and cannot restore my recovery phrase, and If I lose my recovery phrase, I will not be able to access my funds.",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextMedium(
|
||||
context,
|
||||
|
|
|
@ -14,14 +14,9 @@ import 'dart:math';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import '../add_token_view/edit_wallet_tokens_view.dart';
|
||||
import '../new_wallet_options/new_wallet_options_view.dart';
|
||||
import '../new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
|
||||
import '../select_wallet_for_token_view.dart';
|
||||
import 'sub_widgets/word_table.dart';
|
||||
import 'verify_mnemonic_passphrase_dialog.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import '../../../pages_desktop_specific/desktop_home_view.dart';
|
||||
import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import '../../../providers/db/main_db_provider.dart';
|
||||
|
@ -38,7 +33,13 @@ import '../../../wallets/wallet/wallet.dart';
|
|||
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../widgets/desktop/desktop_app_bar.dart';
|
||||
import '../../../widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import '../add_token_view/edit_wallet_tokens_view.dart';
|
||||
import '../new_wallet_options/new_wallet_options_view.dart';
|
||||
import '../new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
|
||||
import '../select_wallet_for_token_view.dart';
|
||||
import 'sub_widgets/word_table.dart';
|
||||
import 'verify_mnemonic_passphrase_dialog.dart';
|
||||
|
||||
final createSpecialEthWalletRoutingFlag = StateProvider((ref) => false);
|
||||
|
||||
|
@ -114,7 +115,8 @@ class _VerifyRecoveryPhraseViewState
|
|||
|
||||
Future<void> _continue(bool isMatch) async {
|
||||
if (isMatch) {
|
||||
if (ref.read(pNewWalletOptions.state).state != null) {
|
||||
if (ref.read(pNewWalletOptions) != null &&
|
||||
ref.read(pNewWalletOptions)!.mnemonicPassphrase.isNotEmpty) {
|
||||
final passphraseVerified = await _verifyMnemonicPassphrase();
|
||||
|
||||
if (!passphraseVerified) {
|
||||
|
|
|
@ -75,6 +75,14 @@ class _NewContactAddressEntryFormState
|
|||
addressLabelFocusNode = FocusNode();
|
||||
addressFocusNode = FocusNode();
|
||||
coins = [...AppConfig.coins];
|
||||
|
||||
if (AppConfig.isSingleCoinApp) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
ref.read(addressEntryDataProvider(widget.id)).coin = coins.first;
|
||||
}
|
||||
});
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -109,7 +117,7 @@ class _NewContactAddressEntryFormState
|
|||
|
||||
return Column(
|
||||
children: [
|
||||
if (isDesktop)
|
||||
if (isDesktop && !AppConfig.isSingleCoinApp)
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<CryptoCurrency>(
|
||||
hint: Text(
|
||||
|
@ -188,7 +196,7 @@ class _NewContactAddressEntryFormState
|
|||
],
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
if (!isDesktop && !AppConfig.isSingleCoinApp)
|
||||
TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
|
@ -280,9 +288,10 @@ class _NewContactAddressEntryFormState
|
|||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (!AppConfig.isSingleCoinApp)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
|
|
@ -1155,7 +1155,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
|||
),
|
||||
if (AppConfig.isStackCoin(selectedCrypto?.ticker))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
text: "Choose from ${AppConfig.prefix}",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = AppConfig.getCryptoCurrencyForTicker(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -194,7 +194,7 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
),
|
||||
if (AppConfig.isStackCoin(model.receiveTicker))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
text: "Choose from ${AppConfig.prefix}",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = AppConfig.coins.firstWhere(
|
||||
|
@ -480,7 +480,7 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
),
|
||||
if (AppConfig.isStackCoin(model.sendTicker))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
text: "Choose from ${AppConfig.prefix}",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = AppConfig.coins.firstWhere(
|
||||
|
|
|
@ -14,10 +14,9 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../app_config.dart';
|
||||
import '../../models/exchange/response_objects/trade.dart';
|
||||
import 'confirm_change_now_send.dart';
|
||||
import '../home_view/home_view.dart';
|
||||
import '../send_view/sub_widgets/building_transaction_dialog.dart';
|
||||
import '../../pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
|
||||
import '../../providers/providers.dart';
|
||||
import '../../route_generator.dart';
|
||||
|
@ -29,12 +28,14 @@ import '../../utilities/amount/amount_formatter.dart';
|
|||
import '../../utilities/assets.dart';
|
||||
import '../../utilities/constants.dart';
|
||||
import '../../utilities/enums/fee_rate_type_enum.dart';
|
||||
import '../../utilities/logger.dart';
|
||||
import '../../utilities/text_styles.dart';
|
||||
import '../../utilities/util.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../wallets/models/tx_data.dart';
|
||||
import '../../wallets/wallet/impl/firo_wallet.dart';
|
||||
import '../../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||
import '../../widgets/background.dart';
|
||||
import '../../widgets/conditional_parent.dart';
|
||||
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
|
@ -43,6 +44,9 @@ import '../../widgets/desktop/desktop_dialog_close_button.dart';
|
|||
import '../../widgets/expandable.dart';
|
||||
import '../../widgets/rounded_white_container.dart';
|
||||
import '../../widgets/stack_dialog.dart';
|
||||
import '../home_view/home_view.dart';
|
||||
import '../send_view/sub_widgets/building_transaction_dialog.dart';
|
||||
import 'confirm_change_now_send.dart';
|
||||
|
||||
class SendFromView extends ConsumerStatefulWidget {
|
||||
const SendFromView({
|
||||
|
@ -135,7 +139,7 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Send from Stack",
|
||||
"Send from ${AppConfig.prefix}",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
|
@ -269,6 +273,15 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
),
|
||||
);
|
||||
|
||||
// Currently CwBasedInterface wallets (xmr/wow) shouldn't even have
|
||||
// access to this screen but this is needed to get past an error that
|
||||
// would occur only to lead to another error which is why xmr/wow wallets
|
||||
// don't have access to this screen currently
|
||||
if (wallet is CwBasedInterface) {
|
||||
await wallet.init();
|
||||
await wallet.open();
|
||||
}
|
||||
|
||||
final time = Future<dynamic>.delayed(
|
||||
const Duration(
|
||||
milliseconds: 2500,
|
||||
|
@ -373,7 +386,8 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
if (mounted) {
|
||||
// pop building dialog
|
||||
Navigator.of(context).pop();
|
||||
|
|
|
@ -190,6 +190,15 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
final showSendFromStackButton = !hasTx &&
|
||||
!["xmr", "monero", "wow", "wownero"]
|
||||
.contains(trade.payInCurrency.toLowerCase()) &&
|
||||
AppConfig.isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
trade.status == "Waiting");
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => Background(
|
||||
|
@ -248,23 +257,13 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
children: children,
|
||||
),
|
||||
),
|
||||
if (!hasTx &&
|
||||
AppConfig.isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
trade.status == "Waiting"))
|
||||
if (showSendFromStackButton)
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
if (!hasTx &&
|
||||
AppConfig.isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
trade.status == "Waiting"))
|
||||
if (showSendFromStackButton)
|
||||
SecondaryButton(
|
||||
label: "Send from Stack",
|
||||
label: "Send from ${AppConfig.prefix}",
|
||||
buttonHeight: ButtonHeight.l,
|
||||
onPressed: () {
|
||||
CryptoCurrency coin;
|
||||
|
@ -1371,15 +1370,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (!isDesktop &&
|
||||
!hasTx &&
|
||||
AppConfig.isStackCoin(trade.payInCurrency) &&
|
||||
(trade.status == "New" ||
|
||||
trade.status == "new" ||
|
||||
trade.status == "waiting" ||
|
||||
trade.status == "Waiting"))
|
||||
if (!isDesktop && showSendFromStackButton)
|
||||
SecondaryButton(
|
||||
label: "Send from Stack",
|
||||
label: "Send from ${AppConfig.prefix}",
|
||||
onPressed: () {
|
||||
CryptoCurrency coin;
|
||||
try {
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
|
||||
import '../../app_config.dart';
|
||||
import '../../providers/global/notifications_provider.dart';
|
||||
import '../../providers/global/prefs_provider.dart';
|
||||
import '../../providers/ui/home_view_index_provider.dart';
|
||||
import '../../providers/ui/unread_notifications_provider.dart';
|
||||
import '../../services/event_bus/events/global/tor_connection_status_changed_event.dart';
|
||||
|
@ -172,6 +173,20 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
// dirty hack
|
||||
ref.listen(
|
||||
prefsChangeNotifierProvider.select((value) => value.enableExchange),
|
||||
(prev, next) {
|
||||
if (next == false &&
|
||||
mounted &&
|
||||
ref.read(homeViewPageIndexStateProvider) != 0) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => ref.read(homeViewPageIndexStateProvider.state).state = 0,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: _onWillPop,
|
||||
child: Background(
|
||||
|
@ -345,7 +360,8 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
),
|
||||
body: Column(
|
||||
children: [
|
||||
if (_children.length > 1)
|
||||
if (_children.length > 1 &&
|
||||
ref.watch(prefsChangeNotifierProvider).enableExchange)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
|
|
|
@ -44,8 +44,6 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//todo: check if print needed
|
||||
// debugPrint("BUILD: HomeViewButtonBar");
|
||||
final selectedIndex = ref.watch(homeViewPageIndexStateProvider.state).state;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
|
|
|
@ -49,6 +49,8 @@ class _IntroViewState extends ConsumerState<IntroView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType ");
|
||||
final stack =
|
||||
ref.watch(themeProvider.select((value) => value.assets.stack));
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
|
@ -68,16 +70,22 @@ class _IntroViewState extends ConsumerState<IntroView> {
|
|||
constraints: const BoxConstraints(
|
||||
maxWidth: 300,
|
||||
),
|
||||
child: SvgPicture.file(
|
||||
File(
|
||||
ref.watch(
|
||||
themeProvider.select(
|
||||
(value) => value.assets.stack,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: isDesktop ? 324 : 266,
|
||||
height: isDesktop ? 324 : 266,
|
||||
child: (stack.endsWith(".png"))
|
||||
? Image.file(
|
||||
File(
|
||||
stack,
|
||||
),
|
||||
)
|
||||
: SvgPicture.file(
|
||||
File(
|
||||
stack,
|
||||
),
|
||||
width: isDesktop ? 324 : 266,
|
||||
height: isDesktop ? 324 : 266,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -163,7 +171,7 @@ class _IntroViewState extends ConsumerState<IntroView> {
|
|||
),
|
||||
if (isDesktop)
|
||||
SecondaryButton(
|
||||
label: "Restore from Stack backup",
|
||||
label: "Restore from ${AppConfig.prefix} backup",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
CreatePasswordView.routeName,
|
||||
|
@ -306,7 +314,7 @@ class GetStartedButton extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
child: Text(
|
||||
"Create new Stack",
|
||||
"Create new ${AppConfig.prefix}",
|
||||
style: STextStyles.button(context).copyWith(fontSize: 20),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
|
||||
import '../../notifications/show_flush_bar.dart';
|
||||
// import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart';
|
||||
|
@ -70,6 +71,7 @@ class LockscreenView extends ConsumerStatefulWidget {
|
|||
|
||||
class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||
late final ShakeController _shakeController;
|
||||
late final bool _autoPin;
|
||||
|
||||
late int _attempts;
|
||||
bool _attemptLock = false;
|
||||
|
@ -186,16 +188,18 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
biometrics = widget.biometrics;
|
||||
_attempts = 0;
|
||||
_timeout = Duration.zero;
|
||||
|
||||
_autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||
if (_autoPin) {
|
||||
_pinTextController.addListener(_onPinChangedAutologinCheck);
|
||||
}
|
||||
_checkUseBiometrics();
|
||||
_pinTextController.addListener(_onPinChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
// _shakeController.dispose();
|
||||
_pinTextController.removeListener(_onPinChanged);
|
||||
_pinTextController.removeListener(_onPinChangedAutologinCheck);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -218,16 +222,120 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
|
||||
final _pinTextController = TextEditingController();
|
||||
|
||||
void _onPinChanged() async {
|
||||
String enteredPin = _pinTextController.text;
|
||||
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||
final autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||
final Mutex _autoPinCheckLock = Mutex();
|
||||
void _onPinChangedAutologinCheck() async {
|
||||
if (mounted) {
|
||||
await _autoPinCheckLock.acquire();
|
||||
}
|
||||
|
||||
if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) {
|
||||
try {
|
||||
if (_autoPin && _pinTextController.text.length >= 4) {
|
||||
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||
if (_pinTextController.text == storedPin) {
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
unawaited(_onUnlock());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
_autoPinCheckLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
void _onSubmitPin(String pin) async {
|
||||
_attempts++;
|
||||
|
||||
if (_attempts > maxAttemptsBeforeThrottling) {
|
||||
_attemptLock = true;
|
||||
switch (_attempts) {
|
||||
case 4:
|
||||
_timeout = const Duration(seconds: 30);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
_timeout = const Duration(seconds: 60);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
_timeout = const Duration(minutes: 5);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
_timeout = const Duration(minutes: 10);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
_timeout = const Duration(minutes: 20);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
_timeout = const Duration(minutes: 30);
|
||||
break;
|
||||
|
||||
default:
|
||||
_timeout = const Duration(minutes: 60);
|
||||
}
|
||||
|
||||
_timer?.cancel();
|
||||
_timer = Timer(_timeout, () {
|
||||
_attemptLock = false;
|
||||
_attempts = 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (_attemptLock) {
|
||||
String prettyTime = "";
|
||||
if (_timeout.inSeconds >= 60) {
|
||||
prettyTime += "${_timeout.inMinutes} minutes";
|
||||
} else {
|
||||
prettyTime += "${_timeout.inSeconds} seconds";
|
||||
}
|
||||
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message:
|
||||
"Incorrect PIN entered too many times. Please wait $prettyTime",
|
||||
context: context,
|
||||
iconAsset: Assets.svg.alertCircle,
|
||||
),
|
||||
);
|
||||
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
|
||||
_pinTextController.text = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||
|
||||
if (storedPin == pin) {
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
unawaited(_onUnlock());
|
||||
} else {
|
||||
unawaited(_shakeController.shake());
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Incorrect PIN. Please try again",
|
||||
context: context,
|
||||
iconAsset: Assets.svg.alertCircle,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
|
||||
_pinTextController.text = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,98 +437,9 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
|||
isRandom: ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.randomizePIN,
|
||||
onSubmit: (String pin) async {
|
||||
_attempts++;
|
||||
|
||||
if (_attempts > maxAttemptsBeforeThrottling) {
|
||||
_attemptLock = true;
|
||||
switch (_attempts) {
|
||||
case 4:
|
||||
_timeout = const Duration(seconds: 30);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
_timeout = const Duration(seconds: 60);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
_timeout = const Duration(minutes: 5);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
_timeout = const Duration(minutes: 10);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
_timeout = const Duration(minutes: 20);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
_timeout = const Duration(minutes: 30);
|
||||
break;
|
||||
|
||||
default:
|
||||
_timeout = const Duration(minutes: 60);
|
||||
}
|
||||
|
||||
_timer?.cancel();
|
||||
_timer = Timer(_timeout, () {
|
||||
_attemptLock = false;
|
||||
_attempts = 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (_attemptLock) {
|
||||
String prettyTime = "";
|
||||
if (_timeout.inSeconds >= 60) {
|
||||
prettyTime += "${_timeout.inMinutes} minutes";
|
||||
} else {
|
||||
prettyTime += "${_timeout.inSeconds} seconds";
|
||||
}
|
||||
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message:
|
||||
"Incorrect PIN entered too many times. Please wait $prettyTime",
|
||||
context: context,
|
||||
iconAsset: Assets.svg.alertCircle,
|
||||
),
|
||||
);
|
||||
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
|
||||
_pinTextController.text = '';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final storedPin =
|
||||
await _secureStore.read(key: 'stack_pin');
|
||||
|
||||
if (storedPin == pin) {
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
unawaited(_onUnlock());
|
||||
} else {
|
||||
unawaited(_shakeController.shake());
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Incorrect PIN. Please try again",
|
||||
context: context,
|
||||
iconAsset: Assets.svg.alertCircle,
|
||||
),
|
||||
);
|
||||
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
|
||||
_pinTextController.text = '';
|
||||
onSubmit: (pin) {
|
||||
if (!_autoPinCheckLock.isLocked) {
|
||||
_onSubmitPin(pin);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
|
||||
import '../../notifications/show_flush_bar.dart';
|
||||
import '../../providers/global/prefs_provider.dart';
|
||||
|
@ -38,6 +39,8 @@ class PinpadDialog extends ConsumerStatefulWidget {
|
|||
class _PinpadDialogState extends ConsumerState<PinpadDialog> {
|
||||
late final ShakeController _shakeController;
|
||||
|
||||
late final bool _autoPin;
|
||||
|
||||
late int _attempts;
|
||||
bool _attemptLock = false;
|
||||
late Duration _timeout;
|
||||
|
@ -63,16 +66,24 @@ class _PinpadDialogState extends ConsumerState<PinpadDialog> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _onPinChanged() async {
|
||||
final enteredPin = _pinTextController.text;
|
||||
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||
final autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||
final Mutex _autoPinCheckLock = Mutex();
|
||||
void _onPinChangedAutologinCheck() async {
|
||||
if (mounted) {
|
||||
await _autoPinCheckLock.acquire();
|
||||
}
|
||||
|
||||
if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) {
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
unawaited(_onUnlock());
|
||||
try {
|
||||
if (_autoPin && _pinTextController.text.length >= 4) {
|
||||
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||
if (_pinTextController.text == storedPin) {
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
unawaited(_onUnlock());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
_autoPinCheckLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,16 +226,19 @@ class _PinpadDialogState extends ConsumerState<PinpadDialog> {
|
|||
biometrics = widget.biometrics;
|
||||
_attempts = 0;
|
||||
_timeout = Duration.zero;
|
||||
_autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||
if (_autoPin) {
|
||||
_pinTextController.addListener(_onPinChangedAutologinCheck);
|
||||
}
|
||||
|
||||
_checkUseBiometrics();
|
||||
_pinTextController.addListener(_onPinChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
// _shakeController.dispose();
|
||||
_pinTextController.removeListener(_onPinChanged);
|
||||
_pinTextController.removeListener(_onPinChangedAutologinCheck);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -276,7 +290,11 @@ class _PinpadDialogState extends ConsumerState<PinpadDialog> {
|
|||
submittedFieldDecoration: _pinPutDecoration,
|
||||
isRandom:
|
||||
ref.read(prefsChangeNotifierProvider).randomizePIN,
|
||||
onSubmit: _onSubmit,
|
||||
onSubmit: (pin) {
|
||||
if (!_autoPinCheckLock.isLocked) {
|
||||
_onSubmit(pin);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
|
|
|
@ -22,6 +22,7 @@ import '../../../utilities/address_utils.dart';
|
|||
import '../../../utilities/text_styles.dart';
|
||||
import '../../../utilities/util.dart';
|
||||
import '../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||
import '../../../widgets/address_private_key.dart';
|
||||
import '../../../widgets/background.dart';
|
||||
import '../../../widgets/conditional_parent.dart';
|
||||
|
@ -371,13 +372,17 @@ class _AddressDetailsViewState extends ConsumerState<AddressDetailsView> {
|
|||
detail: address.subType.prettyName,
|
||||
button: Container(),
|
||||
),
|
||||
const _Div(
|
||||
height: 12,
|
||||
),
|
||||
AddressPrivateKey(
|
||||
walletId: widget.walletId,
|
||||
address: address,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is Bip39HDWallet)
|
||||
const _Div(
|
||||
height: 12,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is Bip39HDWallet)
|
||||
AddressPrivateKey(
|
||||
walletId: widget.walletId,
|
||||
address: address,
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
|
|
|
@ -92,7 +92,6 @@ class _FrostSendViewState extends ConsumerState<FrostSendView> {
|
|||
|
||||
final txData = await wallet.frostCreateSignConfig(
|
||||
txData: TxData(recipients: recipients),
|
||||
changeAddress: (await wallet.getCurrentReceivingAddress())!.value,
|
||||
feePerWeight: customFeeRate,
|
||||
);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -942,6 +942,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
if (isPaynymSend) {
|
||||
sendToController.text = widget.accountLite!.nymName;
|
||||
noteController.text = "PayNym send";
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => _setValidAddressProviders(sendToController.text),
|
||||
);
|
||||
}
|
||||
|
||||
// if (coin is! Epiccash) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../providers/global/prefs_provider.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/constants.dart';
|
||||
|
@ -182,6 +183,55 @@ class AdvancedSettingsView extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
// showExchange pref.
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
return RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Enable exchange features",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 40,
|
||||
child: DraggableSwitchButton(
|
||||
isOn: ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.enableExchange,
|
||||
),
|
||||
),
|
||||
onValueChanged: (newValue) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.enableExchange = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
@ -218,7 +268,7 @@ class AdvancedSettingsView extends StatelessWidget {
|
|||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Stack Experience",
|
||||
text: "${AppConfig.prefix} Experience",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
TextSpan(
|
||||
|
|
|
@ -24,6 +24,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../models/isar/models/log.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/global/debug_service_provider.dart';
|
||||
|
@ -421,7 +422,7 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
},
|
||||
child: CustomLoadingOverlay(
|
||||
message:
|
||||
"Generating Stack logs file",
|
||||
"Generating ${AppConfig.prefix} logs file",
|
||||
eventBus: eventBus,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/block_explorers.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
|
@ -91,7 +92,7 @@ class _ManageExplorerViewState extends ConsumerState<ManageExplorerView> {
|
|||
"every block explorer has a slightly different URL "
|
||||
"scheme.\n\nPaste in your block explorer of choice,"
|
||||
" then edit in [TXID] where the transaction ID "
|
||||
"should go, and Stack Wallet will auto fill the "
|
||||
"should go, and ${AppConfig.appName} will auto fill the "
|
||||
"transaction ID in that place of URL.",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
|
|
|
@ -99,7 +99,7 @@ class GlobalSettingsView extends StatelessWidget {
|
|||
SettingsListButton(
|
||||
iconAssetName: Assets.svg.downloadFolder,
|
||||
iconSize: 14,
|
||||
title: "Stack backup & restore",
|
||||
title: "${AppConfig.prefix} backup & restore",
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
|
@ -113,9 +113,9 @@ class GlobalSettingsView extends StatelessWidget {
|
|||
biometricsCancelButtonString:
|
||||
"CANCEL",
|
||||
biometricsLocalizedReason:
|
||||
"Authenticate to access Stack backup & restore settings",
|
||||
"Authenticate to access ${AppConfig.prefix} backup & restore settings",
|
||||
biometricsAuthenticationTitle:
|
||||
"Stack backup",
|
||||
"${AppConfig.prefix} backup",
|
||||
),
|
||||
settings: const RouteSettings(
|
||||
name: "/swblockscreen",
|
||||
|
|
|
@ -61,12 +61,9 @@ class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
|||
|
||||
int pinCount = 1;
|
||||
|
||||
final TextEditingController _pinTextController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_secureStore = ref.read(secureStoreProvider);
|
||||
_pinTextController.addListener(_onPinChanged);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -77,23 +74,9 @@ class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
|||
_pinPutController2.dispose();
|
||||
_pinPutFocusNode1.dispose();
|
||||
_pinPutFocusNode2.dispose();
|
||||
_pinTextController.removeListener(_onPinChanged);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onPinChanged() async {
|
||||
String enteredPin = _pinTextController.text;
|
||||
final storedPin = await _secureStore.read(key: 'stack_pin');
|
||||
final autoPin = ref.read(prefsChangeNotifierProvider).autoPin;
|
||||
|
||||
if (enteredPin.length >= 4 && autoPin && enteredPin == storedPin) {
|
||||
await _pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.linear,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Background(
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:intl/intl.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../providers/global/auto_swb_service_provider.dart';
|
||||
import '../../../../providers/providers.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -316,7 +317,7 @@ class _AutoBackupViewState extends ConsumerState<AutoBackupView> {
|
|||
children: [
|
||||
const TextSpan(
|
||||
text:
|
||||
"Auto Backup is a custom Stack Wallet feature that offers a convenient backup of your data.\n\nTo ensure maximum security, we recommend using a unique password that you haven't used anywhere else on the internet before. Your password is not stored.\n\nFor more information, please see our website ",
|
||||
"Auto Backup is a custom ${AppConfig.appName} feature that offers a convenient backup of your data.\n\nTo ensure maximum security, we recommend using a unique password that you haven't used anywhere else on the internet before. Your password is not stored.\n\nFor more information, please see our website ",
|
||||
),
|
||||
TextSpan(
|
||||
text: "stackwallet.com.",
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/global/prefs_provider.dart';
|
||||
import '../../../../providers/global/secure_store_provider.dart';
|
||||
|
@ -651,12 +652,12 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup enabled and saved to:",
|
||||
"${AppConfig.prefix} Auto Backup enabled and saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup enabled!",
|
||||
"${AppConfig.prefix} Auto Backup enabled!",
|
||||
),
|
||||
);
|
||||
if (mounted) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/global/secure_store_provider.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -774,7 +775,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
height: 26,
|
||||
),
|
||||
Text(
|
||||
"Stack backup saved to: \n",
|
||||
"${AppConfig.prefix} backup saved to: \n",
|
||||
style: STextStyles
|
||||
.desktopH3(context),
|
||||
),
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../../app_config.dart';
|
||||
import '../../../../../themes/stack_colors.dart';
|
||||
import '../../../../../utilities/text_styles.dart';
|
||||
import '../../../../../utilities/util.dart';
|
||||
|
@ -89,7 +90,7 @@ class CancelStackRestoreDialog extends StatelessWidget {
|
|||
.snackBarBackError,
|
||||
child: Text(
|
||||
"If you cancel, the restore will not complete, and "
|
||||
"the wallets will not appear in your Stack.",
|
||||
"the wallets will not appear in your ${AppConfig.prefix}.",
|
||||
style: STextStyles.desktopTextMedium(context),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -20,6 +20,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../providers/global/prefs_provider.dart';
|
||||
import '../../../../providers/global/secure_store_provider.dart';
|
||||
|
@ -221,10 +222,11 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
barrierDismissible: false,
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title: "Stack Auto Backup saved to:",
|
||||
title: "${AppConfig.prefix} Auto Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(title: "Stack Auto Backup saved"),
|
||||
: const StackOkDialog(
|
||||
title: "${AppConfig.prefix} Auto Backup saved"),
|
||||
);
|
||||
if (mounted) {
|
||||
passwordController.text = "";
|
||||
|
|
|
@ -14,6 +14,8 @@ import 'package:file_picker/file_picker.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
import '../../../../../app_config.dart';
|
||||
import '../../../../../utilities/util.dart';
|
||||
|
||||
class SWBFileSystem {
|
||||
|
@ -39,17 +41,18 @@ class SWBFileSystem {
|
|||
// debugPrint(rootPath!.absolute.toString());
|
||||
|
||||
late Directory sampleFolder;
|
||||
const dirName = "${AppConfig.prefix}_backup";
|
||||
|
||||
if (Platform.isIOS) {
|
||||
sampleFolder = Directory(rootPath!.path);
|
||||
} else if (Platform.isAndroid) {
|
||||
sampleFolder = Directory('${rootPath!.path}Documents/Stack_backups');
|
||||
sampleFolder = Directory('${rootPath!.path}Documents/$dirName');
|
||||
} else if (Platform.isLinux) {
|
||||
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
|
||||
sampleFolder = Directory('${rootPath!.path}/$dirName');
|
||||
} else if (Platform.isWindows) {
|
||||
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
|
||||
sampleFolder = Directory('${rootPath!.path}/$dirName');
|
||||
} else if (Platform.isMacOS) {
|
||||
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
|
||||
sampleFolder = Directory('${rootPath!.path}/$dirName');
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -79,17 +82,20 @@ class SWBFileSystem {
|
|||
}
|
||||
|
||||
Future<void> pickDir(BuildContext context) async {
|
||||
final String? path;
|
||||
final String? chosenPath;
|
||||
if (Platform.isIOS) {
|
||||
path = startPath?.path;
|
||||
chosenPath = startPath?.path;
|
||||
} else {
|
||||
path = await FilePicker.platform.getDirectoryPath(
|
||||
final String path = Platform.isWindows
|
||||
? startPath!.path.replaceAll("/", "\\")
|
||||
: startPath!.path;
|
||||
chosenPath = await FilePicker.platform.getDirectoryPath(
|
||||
dialogTitle: "Choose Backup location",
|
||||
initialDirectory: startPath!.path,
|
||||
initialDirectory: path,
|
||||
lockParentWindow: true,
|
||||
);
|
||||
}
|
||||
dirPath = path;
|
||||
dirPath = chosenPath;
|
||||
}
|
||||
|
||||
Future<void> openFile(BuildContext context) async {
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../route_generator.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -205,7 +206,7 @@ class _RestoreFromEncryptedStringViewState
|
|||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
"Decrypting ${AppConfig.prefix} backup file",
|
||||
style:
|
||||
STextStyles.pageTitleH2(
|
||||
context,
|
||||
|
|
|
@ -338,7 +338,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
"Decrypting ${AppConfig.prefix} backup file",
|
||||
style: STextStyles.pageTitleH2(
|
||||
context,
|
||||
).copyWith(
|
||||
|
@ -452,7 +452,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
"Decrypting ${AppConfig.prefix} backup file",
|
||||
style: STextStyles.pageTitleH2(
|
||||
context,
|
||||
).copyWith(
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'auto_backup_view.dart';
|
||||
import 'create_backup_view.dart';
|
||||
import 'restore_from_file_view.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/constants.dart';
|
||||
|
@ -20,6 +19,9 @@ import '../../../../utilities/text_styles.dart';
|
|||
import '../../../../widgets/background.dart';
|
||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
import 'auto_backup_view.dart';
|
||||
import 'create_backup_view.dart';
|
||||
import 'restore_from_file_view.dart';
|
||||
|
||||
class StackBackupView extends StatelessWidget {
|
||||
const StackBackupView({
|
||||
|
@ -42,7 +44,7 @@ class StackBackupView extends StatelessWidget {
|
|||
},
|
||||
),
|
||||
title: Text(
|
||||
"Stack backup",
|
||||
"${AppConfig.prefix} backup",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
@ -193,7 +170,11 @@ class _Mnemonic extends ConsumerWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
"Please write down your backup key. Keep it safe and never share it with anyone. Your backup key is the only way you can access your funds if you forget your PIN, lose your phone, etc.\n\nStack Wallet does not keep nor is able to restore your backup key. Only you have access to your wallet.",
|
||||
"Please write down your backup key. Keep it safe and never share "
|
||||
"it with anyone. Your backup key is the only way you can access"
|
||||
" your funds if you forget your PIN, lose your phone, etc.\n\n"
|
||||
"${AppConfig.appName} does not keep nor is able to restore your"
|
||||
" backup key. Only you have access to your wallet.",
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
),
|
||||
|
@ -212,10 +193,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 +283,6 @@ class _Mnemonic extends ConsumerWidget {
|
|||
},
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
"Show QR Code",
|
||||
style: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -301,8 +296,6 @@ class _FrostKeys extends StatelessWidget {
|
|||
this.frostWalletData,
|
||||
});
|
||||
|
||||
static const String routeName = "/walletBackup";
|
||||
|
||||
final String walletId;
|
||||
final ({
|
||||
String myName,
|
||||
|
@ -433,19 +426,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 +452,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 +474,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:
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../providers/providers.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
|
@ -69,7 +70,11 @@ class DeleteWalletWarningView extends ConsumerWidget {
|
|||
.extension<StackColors>()!
|
||||
.warningBackground,
|
||||
child: Text(
|
||||
"You are going to permanently delete your wallet.\n\nIf you delete your wallet, the only way you can have access to your funds is by using your backup key.\n\nStack Wallet does not keep nor is able to restore your backup key or your wallet.\n\nPLEASE SAVE YOUR BACKUP KEY.",
|
||||
"You are going to permanently delete your wallet.\n\n"
|
||||
"If you delete your wallet, the only way you can have access"
|
||||
" to your funds is by using your backup key.\n\n"
|
||||
"${AppConfig.appName} does not keep nor is able to restore "
|
||||
"your backup key or your wallet.\n\nPLEASE SAVE YOUR BACKUP KEY.",
|
||||
style: STextStyles.baseXS(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
|
|
@ -11,17 +11,22 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../providers/db/main_db_provider.dart';
|
||||
import '../../../../providers/providers.dart';
|
||||
import '../../../../route_generator.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/constants.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/isar/models/wallet_info.dart';
|
||||
import '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/rbf_interface.dart';
|
||||
import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
|
||||
import '../../../../widgets/background.dart';
|
||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/custom_buttons/draggable_switch_button.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
import '../../../../widgets/stack_dialog.dart';
|
||||
import '../../../pinpad_views/lock_screen_view.dart';
|
||||
|
@ -31,7 +36,7 @@ import 'rbf_settings_view.dart';
|
|||
import 'rename_wallet_view.dart';
|
||||
import 'spark_info.dart';
|
||||
|
||||
class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
||||
class WalletSettingsWalletSettingsView extends ConsumerStatefulWidget {
|
||||
const WalletSettingsWalletSettingsView({
|
||||
super.key,
|
||||
required this.walletId,
|
||||
|
@ -42,7 +47,88 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
final String walletId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<WalletSettingsWalletSettingsView> createState() =>
|
||||
_WalletSettingsWalletSettingsViewState();
|
||||
}
|
||||
|
||||
class _WalletSettingsWalletSettingsViewState
|
||||
extends ConsumerState<WalletSettingsWalletSettingsView> {
|
||||
bool _switchReuseAddressToggledLock = false; // Mutex.
|
||||
Future<void> _switchReuseAddressToggled(bool newValue) async {
|
||||
if (newValue) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
return 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) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
|
@ -80,7 +166,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
RenameWalletView.routeName,
|
||||
arguments: walletId,
|
||||
arguments: widget.walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
|
@ -99,6 +185,172 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is RbfInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is RbfInterface)
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
RbfSettingsView.routeName,
|
||||
arguments: widget.walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"RBF settings",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is MultiAddressInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is MultiAddressInterface)
|
||||
RoundedWhiteContainer(
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
return RawMaterialButton(
|
||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Reuse receiving address",
|
||||
style: STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 40,
|
||||
child: DraggableSwitchButton(
|
||||
isOn: ref.watch(
|
||||
pWalletInfo(widget.walletId).select(
|
||||
(value) => value.otherData),
|
||||
)[WalletInfoKeys.reuseAddress]
|
||||
as bool? ??
|
||||
false,
|
||||
onValueChanged: (newValue) {
|
||||
_switchReuseAddressToggled(newValue);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is LelantusInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is LelantusInterface)
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
LelantusSettingsView.routeName,
|
||||
arguments: widget.walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"Lelantus settings",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is SparkInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(widget.walletId)
|
||||
is SparkInterface)
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
SparkInfoView.routeName,
|
||||
arguments: widget.walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"Spark info",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
@ -119,7 +371,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
context: context,
|
||||
builder: (_) => StackDialog(
|
||||
title:
|
||||
"Do you want to delete ${ref.read(pWalletName(walletId))}?",
|
||||
"Do you want to delete ${ref.read(pWalletName(widget.walletId))}?",
|
||||
leftButton: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -148,7 +400,7 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
shouldUseMaterialRoute:
|
||||
RouteGenerator.useMaterialPageRoute,
|
||||
builder: (_) => LockscreenView(
|
||||
routeOnSuccessArguments: walletId,
|
||||
routeOnSuccessArguments: widget.walletId,
|
||||
showBackButton: true,
|
||||
routeOnSuccess:
|
||||
DeleteWalletWarningView.routeName,
|
||||
|
@ -188,116 +440,6 @@ class WalletSettingsWalletSettingsView extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(walletId)
|
||||
is LelantusInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(walletId)
|
||||
is LelantusInterface)
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
LelantusSettingsView.routeName,
|
||||
arguments: walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"Lelantus settings",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(walletId) is SparkInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(walletId) is SparkInterface)
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
SparkInfoView.routeName,
|
||||
arguments: walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"Spark info",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(walletId) is RbfInterface)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (ref.watch(pWallets).getWallet(walletId) is RbfInterface)
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
RbfSettingsView.routeName,
|
||||
arguments: walletId,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"RBF settings",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -10,14 +10,17 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.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 '../../../../wallets/isar/providers/wallet_info_provider.dart';
|
||||
|
@ -25,16 +28,14 @@ import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interfa
|
|||
import '../../../../widgets/background.dart';
|
||||
import '../../../../widgets/conditional_parent.dart';
|
||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/custom_tab_view.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/detail_item.dart';
|
||||
import '../../../../widgets/qr.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
|
||||
class XPubView extends ConsumerWidget {
|
||||
class XPubView extends ConsumerStatefulWidget {
|
||||
const XPubView({
|
||||
super.key,
|
||||
required this.walletId,
|
||||
|
@ -49,7 +50,39 @@ class XPubView extends ConsumerWidget {
|
|||
static const String routeName = "/xpub";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<XPubView> createState() => XPubViewState();
|
||||
}
|
||||
|
||||
class XPubViewState extends ConsumerState<XPubView> {
|
||||
late String _currentDropDownValue;
|
||||
|
||||
String _current(String key) =>
|
||||
widget.xpubData.xpubs.firstWhere((e) => e.path == key).xpub;
|
||||
|
||||
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.xpubData.xpubs.first.path;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isDesktop = Util.isDesktop;
|
||||
|
||||
return ConditionalParent(
|
||||
|
@ -75,7 +108,25 @@ class XPubView extends ConsumerWidget {
|
|||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
child: SingleChildScrollView(child: child),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) => SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: constraints.maxHeight),
|
||||
child: IntrinsicHeight(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: child,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -95,7 +146,7 @@ class XPubView extends ConsumerWidget {
|
|||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"${ref.watch(pWalletName(walletId))} xpub(s)",
|
||||
"${ref.watch(pWalletName(widget.walletId))} xpub(s)",
|
||||
style: STextStyles.desktopH2(context),
|
||||
),
|
||||
),
|
||||
|
@ -119,28 +170,146 @@ class XPubView extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: Util.isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (isDesktop) const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 12 : 16,
|
||||
),
|
||||
DetailItem(
|
||||
title: "Master fingerprint",
|
||||
detail: xpubData.fingerprint,
|
||||
detail: widget.xpubData.fingerprint,
|
||||
horizontal: true,
|
||||
borderColor: Util.isDesktop
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG
|
||||
: null,
|
||||
),
|
||||
if (isDesktop) const SizedBox(height: 16),
|
||||
CustomTabView(
|
||||
titles: xpubData.xpubs.map((e) => e.path).toList(),
|
||||
children: xpubData.xpubs
|
||||
.map(
|
||||
(e) => Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: _XPub(
|
||||
xpub: e.xpub,
|
||||
derivation: e.path,
|
||||
SizedBox(
|
||||
height: Util.isDesktop ? 12 : 16,
|
||||
),
|
||||
DetailItemBase(
|
||||
horizontal: true,
|
||||
borderColor: Util.isDesktop
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG
|
||||
: null,
|
||||
title: Text(
|
||||
"Derivation",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
detail: SizedBox(
|
||||
width: Util.isDesktop ? 200 : 170,
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<String>(
|
||||
value: _currentDropDownValue,
|
||||
items: [
|
||||
...widget.xpubData.xpubs.map(
|
||||
(e) => DropdownMenuItem(
|
||||
value: e.path,
|
||||
child: Text(
|
||||
e.path,
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -148,91 +317,3 @@ class XPubView extends ConsumerWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _XPub extends StatelessWidget {
|
||||
const _XPub({
|
||||
super.key,
|
||||
required this.xpub,
|
||||
required this.derivation,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
});
|
||||
|
||||
final String xpub;
|
||||
final String derivation;
|
||||
|
||||
final ClipboardInterface clipboardInterface;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool isDesktop = Util.isDesktop;
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 25,
|
||||
),
|
||||
ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) => RoundedWhiteContainer(
|
||||
child: child,
|
||||
),
|
||||
child: QR(
|
||||
data: xpub,
|
||||
size: isDesktop ? 280 : MediaQuery.of(context).size.width / 1.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(16),
|
||||
borderColor:
|
||||
Theme.of(context).extension<StackColors>()!.backgroundAppBar,
|
||||
child: SelectableText(
|
||||
xpub,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Row(
|
||||
children: [
|
||||
if (isDesktop)
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
label: "Cancel",
|
||||
onPressed: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
),
|
||||
if (isDesktop) const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
buttonHeight: ButtonHeight.xl,
|
||||
label: "Copy",
|
||||
onPressed: () async {
|
||||
await clipboardInterface.setData(
|
||||
ClipboardData(
|
||||
text: xpub,
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../db/hive/db.dart';
|
||||
import '../pages_desktop_specific/password/create_password_view.dart';
|
||||
import '../providers/global/prefs_provider.dart';
|
||||
|
@ -104,190 +105,199 @@ class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> {
|
|||
constraints: BoxConstraints(
|
||||
maxWidth: isDesktop ? 480 : double.infinity,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Choose your Stack experience",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopH2(context)
|
||||
: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 8,
|
||||
),
|
||||
Text(
|
||||
!widget.isSettings
|
||||
? "You can change it later in Settings"
|
||||
: "",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopSubtitleH2(context)
|
||||
: STextStyles.subtitle(context),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 32 : 36,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: isDesktop ? 0 : 16,
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => SingleChildScrollView(
|
||||
child: child,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Choose your ${AppConfig.prefix} experience",
|
||||
textAlign: TextAlign.center,
|
||||
style: isDesktop
|
||||
? STextStyles.desktopH2(context)
|
||||
: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
child: PrivacyToggle(
|
||||
externalCallsEnabled: isEasy,
|
||||
onChanged: (externalCalls) {
|
||||
isEasy = externalCalls;
|
||||
setState(() {
|
||||
infoToggle = isEasy;
|
||||
});
|
||||
},
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 8,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 36,
|
||||
),
|
||||
Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(0)
|
||||
: const EdgeInsets.all(16.0),
|
||||
child: RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.label(context).copyWith(
|
||||
fontSize: 12.0,
|
||||
),
|
||||
children: infoToggle
|
||||
? [
|
||||
if (Constants.enableExchange)
|
||||
Text(
|
||||
!widget.isSettings
|
||||
? "You can change it later in Settings"
|
||||
: "",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopSubtitleH2(context)
|
||||
: STextStyles.subtitle(context),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 32 : 36,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: isDesktop ? 0 : 16,
|
||||
),
|
||||
child: PrivacyToggle(
|
||||
externalCallsEnabled: isEasy,
|
||||
onChanged: (externalCalls) {
|
||||
isEasy = externalCalls;
|
||||
setState(() {
|
||||
infoToggle = isEasy;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 36,
|
||||
),
|
||||
Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(0)
|
||||
: const EdgeInsets.all(16.0),
|
||||
child: RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: RichText(
|
||||
textAlign: TextAlign.left,
|
||||
text: TextSpan(
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.label(context).copyWith(
|
||||
fontSize: 12.0,
|
||||
),
|
||||
children: infoToggle
|
||||
? [
|
||||
if (Constants.enableExchange)
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data preloaded for a seamless experience.\n\n",
|
||||
),
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data preloaded for a seamless experience.\n\n",
|
||||
"CoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).\n\n",
|
||||
),
|
||||
const TextSpan(
|
||||
text:
|
||||
"CoinGecko enabled: (24 hour price change shown in-app, total wallet value shown in USD or other currency).\n\n",
|
||||
),
|
||||
TextSpan(
|
||||
text:
|
||||
"Recommended for most crypto users.",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall600(
|
||||
context,
|
||||
)
|
||||
: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
if (Constants.enableExchange)
|
||||
TextSpan(
|
||||
text:
|
||||
"Recommended for most crypto users.",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall600(
|
||||
context,
|
||||
)
|
||||
: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
]
|
||||
: [
|
||||
if (Constants.enableExchange)
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data not preloaded (slower experience).\n\n",
|
||||
),
|
||||
const TextSpan(
|
||||
text:
|
||||
"Exchange data not preloaded (slower experience).\n\n",
|
||||
"CoinGecko disabled (price changes not shown, no wallet value shown in other currencies).\n\n",
|
||||
),
|
||||
const TextSpan(
|
||||
text:
|
||||
"CoinGecko disabled (price changes not shown, no wallet value shown in other currencies).\n\n",
|
||||
),
|
||||
TextSpan(
|
||||
text:
|
||||
"Recommended for the privacy conscious.",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall600(
|
||||
context,
|
||||
)
|
||||
: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
TextSpan(
|
||||
text:
|
||||
"Recommended for the privacy conscious.",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall600(
|
||||
context,
|
||||
)
|
||||
: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isDesktop)
|
||||
const Spacer(
|
||||
flex: 4,
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(0)
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: !widget.isSettings
|
||||
? "Continue"
|
||||
: "Save changes",
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.externalCalls = isEasy;
|
||||
if (!isDesktop)
|
||||
const Spacer(
|
||||
flex: 4,
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(0)
|
||||
: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
label: !widget.isSettings
|
||||
? "Continue"
|
||||
: "Save changes",
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.externalCalls = isEasy;
|
||||
|
||||
DB.instance
|
||||
.put<dynamic>(
|
||||
boxName: DB.boxNamePrefs,
|
||||
key: "externalCalls",
|
||||
value: isEasy,
|
||||
)
|
||||
.then((_) {
|
||||
if (isEasy) {
|
||||
unawaited(
|
||||
ExchangeDataLoadingService.instance
|
||||
.loadAll(),
|
||||
);
|
||||
// unawaited(
|
||||
// BuyDataLoadingService().loadAll(ref));
|
||||
ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.start(true);
|
||||
}
|
||||
});
|
||||
if (!widget.isSettings) {
|
||||
if (isDesktop) {
|
||||
Navigator.of(context).pushNamed(
|
||||
CreatePasswordView.routeName,
|
||||
);
|
||||
DB.instance
|
||||
.put<dynamic>(
|
||||
boxName: DB.boxNamePrefs,
|
||||
key: "externalCalls",
|
||||
value: isEasy,
|
||||
)
|
||||
.then((_) {
|
||||
if (isEasy) {
|
||||
if (AppConfig.hasFeature(AppFeature.swap)) {
|
||||
unawaited(
|
||||
ExchangeDataLoadingService.instance
|
||||
.loadAll(),
|
||||
);
|
||||
}
|
||||
// unawaited(
|
||||
// BuyDataLoadingService().loadAll(ref));
|
||||
ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.start(true);
|
||||
}
|
||||
});
|
||||
if (!widget.isSettings) {
|
||||
if (isDesktop) {
|
||||
Navigator.of(context).pushNamed(
|
||||
CreatePasswordView.routeName,
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
CreatePinView.routeName,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Navigator.of(context).pushNamed(
|
||||
CreatePinView.routeName,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: kDesktopAppBarHeight,
|
||||
),
|
||||
],
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: kDesktopAppBarHeight,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -218,6 +218,9 @@ class TokenWalletOptions extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final prefs = ref.watch(prefsChangeNotifierProvider);
|
||||
final showExchange = prefs.enableExchange;
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
@ -251,11 +254,11 @@ class TokenWalletOptions extends ConsumerWidget {
|
|||
subLabel: "Send",
|
||||
iconAssetPathSVG: Assets.svg.arrowUpRight,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.swap))
|
||||
if (AppConfig.hasFeature(AppFeature.swap) && showExchange)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.swap))
|
||||
if (AppConfig.hasFeature(AppFeature.swap) && showExchange)
|
||||
TokenOptionsButton(
|
||||
onPressed: () => _onExchangePressed(context),
|
||||
subLabel: "Swap",
|
||||
|
@ -265,11 +268,11 @@ class TokenWalletOptions extends ConsumerWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.buy))
|
||||
if (AppConfig.hasFeature(AppFeature.buy) && showExchange)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.buy))
|
||||
if (AppConfig.hasFeature(AppFeature.buy) && showExchange)
|
||||
TokenOptionsButton(
|
||||
onPressed: () => _onBuyPressed(context),
|
||||
subLabel: "Buy",
|
||||
|
|
|
@ -960,8 +960,7 @@ class _DesktopTransactionCardRowState
|
|||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
context: context,
|
||||
message:
|
||||
"Restored Epic funds from your Seed have no Data.\nUse Stack Backup to keep your transaction history.",
|
||||
message: "Restored Epic funds from your Seed have no Data.",
|
||||
type: FlushBarType.warning,
|
||||
duration: const Duration(seconds: 5),
|
||||
),
|
||||
|
|
|
@ -15,14 +15,12 @@ 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 'package:tuple/tuple.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../models/isar/models/blockchain_data/transaction.dart';
|
||||
import '../../../models/isar/models/ethereum/eth_contract.dart';
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import '../../receive_view/addresses/address_details_view.dart';
|
||||
import '../sub_widgets/tx_icon.dart';
|
||||
import 'dialogs/cancelling_transaction_progress_dialog.dart';
|
||||
import 'edit_note_view.dart';
|
||||
import '../wallet_view.dart';
|
||||
import '../../../providers/db/main_db_provider.dart';
|
||||
import '../../../providers/global/address_book_service_provider.dart';
|
||||
import '../../../providers/providers.dart';
|
||||
|
@ -52,8 +50,11 @@ import '../../../widgets/icon_widgets/copy_icon.dart';
|
|||
import '../../../widgets/icon_widgets/pencil_icon.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/stack_dialog.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../receive_view/addresses/address_details_view.dart';
|
||||
import '../sub_widgets/tx_icon.dart';
|
||||
import '../wallet_view.dart';
|
||||
import 'dialogs/cancelling_transaction_progress_dialog.dart';
|
||||
import 'edit_note_view.dart';
|
||||
|
||||
class TransactionDetailsView extends ConsumerStatefulWidget {
|
||||
const TransactionDetailsView({
|
||||
|
@ -133,13 +134,15 @@ class _TransactionDetailsViewState
|
|||
}
|
||||
|
||||
String whatIsIt(Transaction tx, int height) {
|
||||
String prettyConfirms() => "(${tx.getConfirmations(height)}/$minConfirms)";
|
||||
|
||||
final type = tx.type;
|
||||
if (coin is Firo) {
|
||||
if (tx.subType == TransactionSubType.mint) {
|
||||
if (tx.isConfirmed(height, minConfirms)) {
|
||||
return "Minted";
|
||||
} else {
|
||||
return "Minting";
|
||||
return "Minting ${prettyConfirms()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +159,7 @@ class _TransactionDetailsViewState
|
|||
} else if ((_transaction.numberOfMessages ?? 0) > 1) {
|
||||
return "Receiving (waiting for confirmations)"; // TODO test if the sender still has to open again after the receiver has 2 messages present, ie. sender->receiver->sender->node (yes) vs. sender->receiver->node (no)
|
||||
} else {
|
||||
return "Receiving";
|
||||
return "Receiving ${prettyConfirms()}";
|
||||
}
|
||||
}
|
||||
} else if (type == TransactionType.outgoing) {
|
||||
|
@ -168,7 +171,7 @@ class _TransactionDetailsViewState
|
|||
} else if ((_transaction.numberOfMessages ?? 0) > 1) {
|
||||
return "Sending (waiting for confirmations)";
|
||||
} else {
|
||||
return "Sending";
|
||||
return "Sending ${prettyConfirms()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,16 +184,20 @@ class _TransactionDetailsViewState
|
|||
if (tx.isConfirmed(height, minConfirms)) {
|
||||
return "Received";
|
||||
} else {
|
||||
return "Receiving";
|
||||
return "Receiving ${prettyConfirms()}";
|
||||
}
|
||||
} else if (type == TransactionType.outgoing) {
|
||||
if (tx.isConfirmed(height, minConfirms)) {
|
||||
return "Sent";
|
||||
} else {
|
||||
return "Sending";
|
||||
return "Sending ${prettyConfirms()}";
|
||||
}
|
||||
} else if (type == TransactionType.sentToSelf) {
|
||||
return "Sent to self";
|
||||
if (tx.isConfirmed(height, minConfirms)) {
|
||||
return "Sent to self";
|
||||
} else {
|
||||
return "Sent to self ${prettyConfirms()}";
|
||||
}
|
||||
} else {
|
||||
return type.name;
|
||||
}
|
||||
|
@ -1157,59 +1164,95 @@ class _TransactionDetailsViewState
|
|||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final String height;
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final String height;
|
||||
final String confirmations;
|
||||
final confirms = _transaction.getConfirmations(
|
||||
currentHeight,
|
||||
);
|
||||
|
||||
if (widget.coin is Bitcoincash ||
|
||||
widget.coin is Ecash) {
|
||||
if (widget.coin is Bitcoincash ||
|
||||
widget.coin is Ecash) {
|
||||
height = _transaction.height != null &&
|
||||
_transaction.height! > 0
|
||||
? "${_transaction.height!}"
|
||||
: "Pending";
|
||||
confirmations = confirms.toString();
|
||||
} else if (widget.coin is Epiccash &&
|
||||
_transaction.slateId == null) {
|
||||
confirmations = "Unknown";
|
||||
height = "Unknown";
|
||||
} else {
|
||||
final confirmed = _transaction.isConfirmed(
|
||||
currentHeight,
|
||||
minConfirms,
|
||||
);
|
||||
if (widget.coin is! Epiccash && confirmed) {
|
||||
height =
|
||||
"${_transaction.height != null && _transaction.height! > 0 ? _transaction.height! : "Pending"}";
|
||||
"${_transaction.height == 0 ? "Unknown" : _transaction.height}";
|
||||
} else {
|
||||
height = widget.coin is! Epiccash &&
|
||||
_transaction.isConfirmed(
|
||||
currentHeight,
|
||||
minConfirms,
|
||||
)
|
||||
? "${_transaction.height == 0 ? "Unknown" : _transaction.height}"
|
||||
: _transaction.getConfirmations(
|
||||
currentHeight,
|
||||
) >
|
||||
0
|
||||
? "${_transaction.height}"
|
||||
: "Pending";
|
||||
height = confirms > 0
|
||||
? "${_transaction.height}"
|
||||
: "Pending";
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
confirmations = confirms.toString();
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Block height",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.itemSubtitle(
|
||||
context,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Block height",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.itemSubtitle(
|
||||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
SelectableText(
|
||||
height,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles
|
||||
.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
height,
|
||||
style: isDesktop
|
||||
|
@ -1226,30 +1269,91 @@ class _TransactionDetailsViewState
|
|||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(data: height),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
height,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Confirmations",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.itemSubtitle(
|
||||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(data: height),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
if (isDesktop)
|
||||
SelectableText(
|
||||
confirmations,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles
|
||||
.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
confirmations,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(data: height),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
if (coin is Ethereum)
|
||||
isDesktop
|
||||
|
|
|
@ -1053,6 +1053,12 @@ class _TransactionV2DetailsViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
if (coin is Epiccash)
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (coin is Epiccash)
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
|
@ -1457,59 +1463,95 @@ class _TransactionV2DetailsViewState
|
|||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final String height;
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final String height;
|
||||
final String confirmations;
|
||||
final confirms = _transaction.getConfirmations(
|
||||
currentHeight,
|
||||
);
|
||||
|
||||
if (widget.coin is Bitcoincash ||
|
||||
widget.coin is Ecash) {
|
||||
if (widget.coin is Bitcoincash ||
|
||||
widget.coin is Ecash) {
|
||||
height = _transaction.height != null &&
|
||||
_transaction.height! > 0
|
||||
? "${_transaction.height!}"
|
||||
: "Pending";
|
||||
confirmations = confirms.toString();
|
||||
} else if (widget.coin is Epiccash &&
|
||||
_transaction.slateId == null) {
|
||||
confirmations = "Unknown";
|
||||
height = "Unknown";
|
||||
} else {
|
||||
final confirmed = _transaction.isConfirmed(
|
||||
currentHeight,
|
||||
minConfirms,
|
||||
);
|
||||
if (widget.coin is! Epiccash && confirmed) {
|
||||
height =
|
||||
"${_transaction.height != null && _transaction.height! > 0 ? _transaction.height! : "Pending"}";
|
||||
"${_transaction.height == 0 ? "Unknown" : _transaction.height}";
|
||||
} else {
|
||||
height = widget.coin is! Epiccash &&
|
||||
_transaction.isConfirmed(
|
||||
currentHeight,
|
||||
minConfirms,
|
||||
)
|
||||
? "${_transaction.height == 0 ? "Unknown" : _transaction.height}"
|
||||
: _transaction.getConfirmations(
|
||||
currentHeight,
|
||||
) >
|
||||
0
|
||||
? "${_transaction.height}"
|
||||
: "Pending";
|
||||
height = confirms > 0
|
||||
? "${_transaction.height}"
|
||||
: "Pending";
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
confirmations = confirms.toString();
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Block height",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.itemSubtitle(
|
||||
context,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Block height",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.itemSubtitle(
|
||||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
SelectableText(
|
||||
height,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles
|
||||
.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (isDesktop)
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
height,
|
||||
style: isDesktop
|
||||
|
@ -1526,30 +1568,91 @@ class _TransactionV2DetailsViewState
|
|||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(data: height),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
height,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
isDesktop
|
||||
? const _Divider()
|
||||
: const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Confirmations",
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
)
|
||||
: STextStyles.itemSubtitle(
|
||||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(data: height),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
if (isDesktop)
|
||||
SelectableText(
|
||||
confirmations,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(
|
||||
context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles
|
||||
.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isDesktop)
|
||||
SelectableText(
|
||||
confirmations,
|
||||
style: isDesktop
|
||||
? STextStyles
|
||||
.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.textDark,
|
||||
)
|
||||
: STextStyles.itemSubtitle12(
|
||||
context,
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
IconCopyButton(data: height),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
if (kDebugMode)
|
||||
|
|
|
@ -518,6 +518,9 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
|
||||
final coin = ref.watch(pWalletCoin(walletId));
|
||||
|
||||
final prefs = ref.watch(prefsChangeNotifierProvider);
|
||||
final showExchange = prefs.enableExchange;
|
||||
|
||||
return ConditionalParent(
|
||||
condition: _rescanningOnOpen,
|
||||
builder: (child) {
|
||||
|
@ -1053,7 +1056,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
),
|
||||
if (Constants.enableExchange &&
|
||||
ref.watch(pWalletCoin(walletId)) is! FrostCurrency &&
|
||||
AppConfig.hasFeature(AppFeature.swap))
|
||||
AppConfig.hasFeature(AppFeature.swap) &&
|
||||
showExchange)
|
||||
WalletNavigationBarItemData(
|
||||
label: "Swap",
|
||||
icon: const ExchangeNavIcon(),
|
||||
|
@ -1061,7 +1065,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
),
|
||||
if (Constants.enableExchange &&
|
||||
ref.watch(pWalletCoin(walletId)) is! FrostCurrency &&
|
||||
AppConfig.hasFeature(AppFeature.buy))
|
||||
AppConfig.hasFeature(AppFeature.buy) &&
|
||||
showExchange)
|
||||
WalletNavigationBarItemData(
|
||||
label: "Buy",
|
||||
icon: const BuyNavIcon(),
|
||||
|
|
|
@ -33,6 +33,9 @@ class EmptyWallets extends ConsumerWidget {
|
|||
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
final stack =
|
||||
ref.watch(themeProvider.select((value) => value.assets.stack));
|
||||
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
@ -47,21 +50,28 @@ class EmptyWallets extends ConsumerWidget {
|
|||
const Spacer(
|
||||
flex: 2,
|
||||
),
|
||||
SvgPicture.file(
|
||||
File(
|
||||
ref.watch(
|
||||
themeProvider.select(
|
||||
(value) => value.assets.stack,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: isDesktop ? 324 : MediaQuery.of(context).size.width / 3,
|
||||
child: (stack.endsWith(".png"))
|
||||
? Image.file(
|
||||
File(
|
||||
stack,
|
||||
),
|
||||
)
|
||||
: SvgPicture.file(
|
||||
File(
|
||||
stack,
|
||||
),
|
||||
width: isDesktop
|
||||
? 324
|
||||
: MediaQuery.of(context).size.width / 3,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 30 : 16,
|
||||
),
|
||||
Text(
|
||||
"You do not have any wallets yet. Start building your crypto Stack!",
|
||||
AppConfig.emptyWalletsMessage,
|
||||
textAlign: TextAlign.center,
|
||||
style: isDesktop
|
||||
? STextStyles.desktopSubtitleH2(context).copyWith(
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../app_config.dart';
|
||||
import '../../models/add_wallet_list_entity/sub_classes/coin_entity.dart';
|
||||
|
@ -20,6 +19,8 @@ import '../../models/isar/models/ethereum/eth_contract.dart';
|
|||
import '../../pages_desktop_specific/my_stack_view/dialogs/desktop_expanding_wallet_card.dart';
|
||||
import '../../providers/db/main_db_provider.dart';
|
||||
import '../../providers/providers.dart';
|
||||
import '../../services/event_bus/events/wallet_added_event.dart';
|
||||
import '../../services/event_bus/global_event_bus.dart';
|
||||
import '../../themes/stack_colors.dart';
|
||||
import '../../utilities/assets.dart';
|
||||
import '../../utilities/constants.dart';
|
||||
|
@ -58,6 +59,8 @@ class WalletsOverview extends ConsumerStatefulWidget {
|
|||
ConsumerState<WalletsOverview> createState() => _EthWalletsOverviewState();
|
||||
}
|
||||
|
||||
typedef WalletListItemData = ({Wallet wallet, List<EthContract> contracts});
|
||||
|
||||
class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
|
@ -66,28 +69,29 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
|||
|
||||
String _searchString = "";
|
||||
|
||||
final List<Tuple2<Wallet, List<EthContract>>> wallets = [];
|
||||
final Map<String, WalletListItemData> wallets = {};
|
||||
|
||||
List<Tuple2<Wallet, List<EthContract>>> _filter(String searchTerm) {
|
||||
List<WalletListItemData> _filter(String searchTerm) {
|
||||
if (searchTerm.isEmpty) {
|
||||
return wallets;
|
||||
return wallets.values.toList()
|
||||
..sort((a, b) => a.wallet.info.name.compareTo(b.wallet.info.name));
|
||||
}
|
||||
|
||||
final List<Tuple2<Wallet, List<EthContract>>> results = [];
|
||||
final Map<String, WalletListItemData> results = {};
|
||||
final term = searchTerm.toLowerCase();
|
||||
|
||||
for (final tuple in wallets) {
|
||||
for (final entry in wallets.entries) {
|
||||
bool includeManager = false;
|
||||
// search wallet name and total balance
|
||||
includeManager |= _elementContains(tuple.item1.info.name, term);
|
||||
includeManager |= _elementContains(entry.value.wallet.info.name, term);
|
||||
includeManager |= _elementContains(
|
||||
tuple.item1.info.cachedBalance.total.decimal.toString(),
|
||||
entry.value.wallet.info.cachedBalance.total.decimal.toString(),
|
||||
term,
|
||||
);
|
||||
|
||||
final List<EthContract> contracts = [];
|
||||
|
||||
for (final contract in tuple.item2) {
|
||||
for (final contract in entry.value.contracts) {
|
||||
if (_elementContains(contract.name, term)) {
|
||||
contracts.add(contract);
|
||||
} else if (_elementContains(contract.symbol, term)) {
|
||||
|
@ -100,22 +104,19 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
|||
}
|
||||
|
||||
if (includeManager || contracts.isNotEmpty) {
|
||||
results.add(Tuple2(tuple.item1, contracts));
|
||||
results.addEntries([entry]);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
return results.values.toList()
|
||||
..sort((a, b) => a.wallet.info.name.compareTo(b.wallet.info.name));
|
||||
}
|
||||
|
||||
bool _elementContains(String element, String term) {
|
||||
return element.toLowerCase().contains(term);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
searchFieldFocusNode = FocusNode();
|
||||
|
||||
void updateWallets() {
|
||||
final walletsData =
|
||||
ref.read(mainDBProvider).isar.walletInfo.where().findAllSync();
|
||||
walletsData.removeWhere((e) => e.coin != widget.coin);
|
||||
|
@ -143,28 +144,48 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
|||
}
|
||||
|
||||
// add tuple to list
|
||||
wallets.add(
|
||||
Tuple2(
|
||||
ref.read(pWallets).getWallet(
|
||||
data.walletId,
|
||||
),
|
||||
contracts,
|
||||
),
|
||||
wallets[data.walletId] = (
|
||||
wallet: ref.read(pWallets).getWallet(
|
||||
data.walletId,
|
||||
),
|
||||
contracts: contracts,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// add non token wallet tuple to list
|
||||
for (final data in walletsData) {
|
||||
wallets.add(
|
||||
Tuple2(
|
||||
ref.read(pWallets).getWallet(
|
||||
// desktop single coin apps may cause issues so lets just ignore the error and move on
|
||||
try {
|
||||
wallets[data.walletId] = (
|
||||
wallet: ref.read(pWallets).getWallet(
|
||||
data.walletId,
|
||||
),
|
||||
[],
|
||||
),
|
||||
);
|
||||
contracts: [],
|
||||
);
|
||||
} catch (_) {
|
||||
// lol bandaid for single coin based apps
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
searchFieldFocusNode = FocusNode();
|
||||
|
||||
updateWallets();
|
||||
|
||||
if (AppConfig.isSingleCoinApp) {
|
||||
GlobalEventBus.instance.on<WalletAddedEvent>().listen((_) {
|
||||
updateWallets();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
@ -293,24 +314,27 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
|||
final data = _filter(_searchString);
|
||||
return ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
final element = data[index];
|
||||
final entry = data[index];
|
||||
final wallet = entry.wallet;
|
||||
|
||||
if (element.item1.cryptoCurrency.hasTokenSupport) {
|
||||
if (wallet.cryptoCurrency.hasTokenSupport) {
|
||||
if (isDesktop) {
|
||||
return DesktopExpandingWalletCard(
|
||||
key: Key(
|
||||
"${element.item1.info.name}_${element.item2.map((e) => e.address).join()}",
|
||||
"${wallet.walletId}_${entry.contracts.map((e) => e.address).join()}",
|
||||
),
|
||||
data: element,
|
||||
data: entry,
|
||||
navigatorState: widget.navigatorState!,
|
||||
);
|
||||
} else {
|
||||
return MasterWalletCard(
|
||||
walletId: element.item1.walletId,
|
||||
key: Key(wallet.walletId),
|
||||
walletId: wallet.walletId,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return ConditionalParent(
|
||||
key: Key(wallet.walletId),
|
||||
condition: isDesktop,
|
||||
builder: (child) => RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
@ -323,7 +347,7 @@ class _EthWalletsOverviewState extends ConsumerState<WalletsOverview> {
|
|||
child: child,
|
||||
),
|
||||
child: SimpleWalletCard(
|
||||
walletId: element.item1.walletId,
|
||||
walletId: wallet.walletId,
|
||||
popPrevious: widget
|
||||
.overrideSimpleWalletCardPopPreviousValueWith ==
|
||||
null
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -206,7 +206,8 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
|
|||
void sendFromStack() {
|
||||
final trade = ref.read(desktopExchangeModelProvider)!.trade!;
|
||||
final address = trade.payInAddress;
|
||||
final coin = AppConfig.getCryptoCurrencyForTicker(trade.payInCurrency)!;
|
||||
final coin = AppConfig.getCryptoCurrencyForTicker(trade.payInCurrency) ??
|
||||
AppConfig.getCryptoCurrencyByPrettyName(trade.payInCurrency);
|
||||
final amount = Decimal.parse(trade.payInAmount).toAmount(
|
||||
fractionDigits: coin.fractionDigits,
|
||||
);
|
||||
|
|
|
@ -326,7 +326,7 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
|||
),
|
||||
))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
text: "Choose from ${AppConfig.prefix}",
|
||||
onTap: selectRecipientAddressFromStack,
|
||||
),
|
||||
],
|
||||
|
@ -472,7 +472,7 @@ class _DesktopStep2State extends ConsumerState<DesktopStep2> {
|
|||
),
|
||||
))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
text: "Choose from ${AppConfig.prefix}",
|
||||
onTap: selectRefundAddressFromStack,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../app_config.dart';
|
||||
import '../../../providers/providers.dart';
|
||||
import '../../../themes/stack_colors.dart';
|
||||
import '../../../utilities/amount/amount.dart';
|
||||
|
@ -87,7 +88,7 @@ class _DesktopChooseFromStackState
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Choose from Stack",
|
||||
"Choose from ${AppConfig.prefix}",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -11,16 +11,15 @@
|
|||
import 'dart:io';
|
||||
|
||||
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 '../providers/desktop/current_desktop_menu_item.dart';
|
||||
import '../providers/providers.dart';
|
||||
import '../themes/stack_colors.dart';
|
||||
import '../utilities/assets.dart';
|
||||
import '../utilities/text_styles.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../widgets/desktop/desktop_tor_status_button.dart';
|
||||
import '../widgets/desktop/living_stack_icon.dart';
|
||||
import 'desktop_menu_item.dart';
|
||||
|
@ -60,6 +59,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
late final DMIController torButtonController;
|
||||
|
||||
double _width = expandedWidth;
|
||||
bool get _isMinimized => _width < expandedWidth;
|
||||
|
||||
void updateSelectedMenuItem(DesktopMenuItemId idKey) {
|
||||
widget.onSelectionWillChange?.call(idKey);
|
||||
|
@ -114,6 +114,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final prefs = ref.watch(prefsChangeNotifierProvider);
|
||||
|
||||
final showExchange = prefs.enableExchange;
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
child: AnimatedContainer(
|
||||
|
@ -163,7 +167,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
onPressed: () {
|
||||
ref.read(currentDesktopMenuItemProvider.state).state =
|
||||
DesktopMenuItemId.settings;
|
||||
ref.watch(selectedSettingsMenuItemStateProvider.state).state =
|
||||
ref.read(selectedSettingsMenuItemStateProvider.state).state =
|
||||
4;
|
||||
},
|
||||
),
|
||||
|
@ -181,114 +185,134 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('myStack'),
|
||||
duration: duration,
|
||||
icon: const DesktopMyStackIcon(),
|
||||
label: "My ${AppConfig.prefix}",
|
||||
value: DesktopMenuItemId.myStack,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[0],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.swap))
|
||||
if (AppConfig.hasFeature(AppFeature.swap) &&
|
||||
showExchange) ...[
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.swap))
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('swap'),
|
||||
duration: duration,
|
||||
icon: const DesktopExchangeIcon(),
|
||||
label: "Swap",
|
||||
value: DesktopMenuItemId.exchange,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[1],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.buy))
|
||||
],
|
||||
if (AppConfig.hasFeature(AppFeature.buy) &&
|
||||
showExchange) ...[
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
if (AppConfig.hasFeature(AppFeature.buy))
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('buy'),
|
||||
duration: duration,
|
||||
icon: const DesktopBuyIcon(),
|
||||
label: "Buy crypto",
|
||||
value: DesktopMenuItemId.buy,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[2],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
],
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('notifications'),
|
||||
duration: duration,
|
||||
icon: const DesktopNotificationsIcon(),
|
||||
label: "Notifications",
|
||||
value: DesktopMenuItemId.notifications,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[3],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('addressBook'),
|
||||
duration: duration,
|
||||
icon: const DesktopAddressBookIcon(),
|
||||
label: "Address Book",
|
||||
value: DesktopMenuItemId.addressBook,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[4],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('settings'),
|
||||
duration: duration,
|
||||
icon: const DesktopSettingsIcon(),
|
||||
label: "Settings",
|
||||
value: DesktopMenuItemId.settings,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[5],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('support'),
|
||||
duration: duration,
|
||||
icon: const DesktopSupportIcon(),
|
||||
label: "Support",
|
||||
value: DesktopMenuItemId.support,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[6],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('about'),
|
||||
duration: duration,
|
||||
icon: const DesktopAboutIcon(),
|
||||
label: "About",
|
||||
value: DesktopMenuItemId.about,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
controller: controllers[7],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
const Spacer(),
|
||||
if (!Platform.isIOS)
|
||||
DesktopMenuItem(
|
||||
key: const ValueKey('exit'),
|
||||
duration: duration,
|
||||
labelLength: 123,
|
||||
icon: const DesktopExitIcon(),
|
||||
label: "Exit",
|
||||
value: 7,
|
||||
onChanged: (_) {
|
||||
// todo: save stuff/ notify before exit?
|
||||
if (AppConfig.coins
|
||||
.where((e) => e is Monero || e is Wownero)
|
||||
.isNotEmpty) {
|
||||
// hack to insta kill because xmr/wow native lib code sucks
|
||||
exit(0);
|
||||
} else {
|
||||
SystemNavigator.pop();
|
||||
}
|
||||
// // todo: save stuff/ notify before exit?
|
||||
// if (AppConfig.coins
|
||||
// .where((e) => e is Monero || e is Wownero)
|
||||
// .isNotEmpty) {
|
||||
// // hack to insta kill because xmr/wow native lib code sucks
|
||||
exit(0);
|
||||
// } else {
|
||||
// SystemNavigator.pop();
|
||||
// }
|
||||
},
|
||||
controller: controllers[8],
|
||||
isExpandedInitially: !_isMinimized,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -24,6 +24,9 @@ import 'desktop_menu.dart';
|
|||
|
||||
class DMIController {
|
||||
VoidCallback? toggle;
|
||||
|
||||
DMIController();
|
||||
|
||||
void dispose() {
|
||||
toggle = null;
|
||||
}
|
||||
|
@ -237,6 +240,7 @@ class DesktopMenuItem<T> extends ConsumerStatefulWidget {
|
|||
required this.duration,
|
||||
this.labelLength = 125,
|
||||
this.controller,
|
||||
required this.isExpandedInitially,
|
||||
});
|
||||
|
||||
final Widget icon;
|
||||
|
@ -246,6 +250,7 @@ class DesktopMenuItem<T> extends ConsumerStatefulWidget {
|
|||
final Duration duration;
|
||||
final double labelLength;
|
||||
final DMIController? controller;
|
||||
final bool isExpandedInitially;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopMenuItem<T>> createState() => _DesktopMenuItemState<T>();
|
||||
|
@ -287,11 +292,17 @@ class _DesktopMenuItemState<T> extends ConsumerState<DesktopMenuItem<T>>
|
|||
labelLength = widget.labelLength;
|
||||
controller = widget.controller;
|
||||
|
||||
_iconOnly = !widget.isExpandedInitially;
|
||||
controller?.toggle = toggle;
|
||||
animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: duration,
|
||||
)..forward();
|
||||
);
|
||||
if (_iconOnly) {
|
||||
animationController.value = 0;
|
||||
} else {
|
||||
animationController.value = 1;
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
|
|
@ -11,19 +11,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../../models/isar/models/ethereum/eth_contract.dart';
|
||||
|
||||
import '../../../pages/wallets_view/wallets_overview.dart';
|
||||
import '../../../themes/stack_colors.dart';
|
||||
import '../../../utilities/assets.dart';
|
||||
import '../../../utilities/constants.dart';
|
||||
import '../../../utilities/text_styles.dart';
|
||||
import '../../../wallets/wallet/wallet.dart';
|
||||
import '../../../widgets/animated_widgets/rotate_icon.dart';
|
||||
import '../../../widgets/expandable.dart';
|
||||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/wallet_card.dart';
|
||||
import '../../../widgets/wallet_info_row/sub_widgets/wallet_info_row_balance.dart';
|
||||
import '../../../widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class DesktopExpandingWalletCard extends StatefulWidget {
|
||||
const DesktopExpandingWalletCard({
|
||||
|
@ -32,7 +31,7 @@ class DesktopExpandingWalletCard extends StatefulWidget {
|
|||
required this.navigatorState,
|
||||
});
|
||||
|
||||
final Tuple2<Wallet, List<EthContract>> data;
|
||||
final WalletListItemData data;
|
||||
final NavigatorState navigatorState;
|
||||
|
||||
@override
|
||||
|
@ -48,9 +47,9 @@ class _DesktopExpandingWalletCardState
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.data.item1.cryptoCurrency.hasTokenSupport) {
|
||||
if (widget.data.wallet.cryptoCurrency.hasTokenSupport) {
|
||||
tokenContractAddresses.addAll(
|
||||
widget.data.item2.map((e) => e.address),
|
||||
widget.data.contracts.map((e) => e.address),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -63,7 +62,7 @@ class _DesktopExpandingWalletCardState
|
|||
padding: EdgeInsets.zero,
|
||||
borderColor: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
|
||||
child: Expandable(
|
||||
initialState: widget.data.item1.cryptoCurrency.hasTokenSupport
|
||||
initialState: widget.data.wallet.cryptoCurrency.hasTokenSupport
|
||||
? ExpandableState.expanded
|
||||
: ExpandableState.collapsed,
|
||||
controller: expandableController,
|
||||
|
@ -89,13 +88,13 @@ class _DesktopExpandingWalletCardState
|
|||
child: Row(
|
||||
children: [
|
||||
WalletInfoCoinIcon(
|
||||
coin: widget.data.item1.info.coin,
|
||||
coin: widget.data.wallet.info.coin,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
widget.data.item1.info.name,
|
||||
widget.data.wallet.info.name,
|
||||
style: STextStyles.desktopTextExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
|
@ -109,7 +108,7 @@ class _DesktopExpandingWalletCardState
|
|||
Expanded(
|
||||
flex: 4,
|
||||
child: WalletInfoRowBalance(
|
||||
walletId: widget.data.item1.walletId,
|
||||
walletId: widget.data.wallet.walletId,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -173,7 +172,7 @@ class _DesktopExpandingWalletCardState
|
|||
bottom: 14,
|
||||
),
|
||||
child: SimpleWalletCard(
|
||||
walletId: widget.data.item1.walletId,
|
||||
walletId: widget.data.wallet.walletId,
|
||||
popPrevious: true,
|
||||
desktopNavigatorState: widget.navigatorState,
|
||||
),
|
||||
|
@ -187,7 +186,7 @@ class _DesktopExpandingWalletCardState
|
|||
bottom: 14,
|
||||
),
|
||||
child: SimpleWalletCard(
|
||||
walletId: widget.data.item1.walletId,
|
||||
walletId: widget.data.wallet.walletId,
|
||||
contractAddress: e,
|
||||
popPrevious: true,
|
||||
desktopNavigatorState: widget.navigatorState,
|
||||
|
|
|
@ -182,7 +182,9 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
final wallet = ref.read(pWallets).getWallet(walletId);
|
||||
supportsSpark = ref.read(pWallets).getWallet(walletId) is SparkInterface;
|
||||
showMultiType = supportsSpark ||
|
||||
ref.read(pWallets).getWallet(walletId) is MultiAddressInterface;
|
||||
(wallet is! BCashInterface &&
|
||||
wallet is Bip39HDWallet &&
|
||||
wallet.supportedAddressTypes.length > 1);
|
||||
|
||||
_walletAddressTypes.add(wallet.info.mainAddressType);
|
||||
|
||||
|
@ -338,7 +340,7 @@ class _DesktopReceiveState extends ConsumerState<DesktopReceive> {
|
|||
onTap: () {
|
||||
clipboard.setData(
|
||||
ClipboardData(
|
||||
text: ref.watch(pWalletReceivingAddress(walletId)),
|
||||
text: address,
|
||||
),
|
||||
);
|
||||
showFloatingFlushBar(
|
||||
|
|
|
@ -353,6 +353,9 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> {
|
|||
final wallet = ref.watch(pWallets).getWallet(widget.walletId);
|
||||
final coin = wallet.info.coin;
|
||||
|
||||
final prefs = ref.watch(prefsChangeNotifierProvider);
|
||||
final showExchange = prefs.enableExchange;
|
||||
|
||||
final showMore = wallet is PaynymInterface ||
|
||||
(wallet is CoinControlInterface &&
|
||||
ref.watch(
|
||||
|
@ -368,7 +371,9 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> {
|
|||
|
||||
return Row(
|
||||
children: [
|
||||
if (Constants.enableExchange && AppConfig.hasFeature(AppFeature.swap))
|
||||
if (Constants.enableExchange &&
|
||||
AppConfig.hasFeature(AppFeature.swap) &&
|
||||
showExchange)
|
||||
SecondaryButton(
|
||||
label: "Swap",
|
||||
width: buttonWidth,
|
||||
|
@ -383,11 +388,15 @@ class _DesktopWalletFeaturesState extends ConsumerState<DesktopWalletFeatures> {
|
|||
),
|
||||
onPressed: () => _onSwapPressed(),
|
||||
),
|
||||
if (Constants.enableExchange && AppConfig.hasFeature(AppFeature.buy))
|
||||
if (Constants.enableExchange &&
|
||||
AppConfig.hasFeature(AppFeature.buy) &&
|
||||
showExchange)
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
if (Constants.enableExchange && AppConfig.hasFeature(AppFeature.buy))
|
||||
if (Constants.enableExchange &&
|
||||
AppConfig.hasFeature(AppFeature.buy) &&
|
||||
showExchange)
|
||||
SecondaryButton(
|
||||
label: "Buy",
|
||||
width: buttonWidth,
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../../../../../app_config.dart';
|
||||
import '../../../../../db/sqlite/firo_cache.dart';
|
||||
import '../../../../../providers/db/main_db_provider.dart';
|
||||
import '../../../../../providers/global/prefs_provider.dart';
|
||||
|
@ -19,6 +20,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,6 +34,8 @@ 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';
|
||||
|
||||
class MoreFeaturesDialog extends ConsumerStatefulWidget {
|
||||
|
@ -102,6 +106,117 @@ 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 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",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).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(
|
||||
|
@ -167,7 +282,7 @@ class _MoreFeaturesDialogState extends ConsumerState<MoreFeaturesDialog> {
|
|||
if (wallet is OrdinalsInterface)
|
||||
_MoreFeaturesItem(
|
||||
label: "Ordinals",
|
||||
detail: "View and control your ordinals in Stack",
|
||||
detail: "View and control your ordinals in ${AppConfig.prefix}",
|
||||
iconAsset: Assets.svg.ordinal,
|
||||
onPressed: () async => widget.onOrdinalsPressed?.call(),
|
||||
),
|
||||
|
@ -253,6 +368,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",
|
||||
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,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
import '../../app_config.dart';
|
||||
import '../../db/hive/db.dart';
|
||||
import '../../notifications/show_flush_bar.dart';
|
||||
import '../../pages/intro_view.dart';
|
||||
|
@ -143,7 +144,7 @@ class _ForgotPasswordDesktopViewState
|
|||
),
|
||||
TextSpan(
|
||||
text: widget.shouldCreateNew
|
||||
? "create a new Stack"
|
||||
? "create a new ${AppConfig.prefix}"
|
||||
: "restore from backup",
|
||||
style: STextStyles.desktopTextSmallBold(context),
|
||||
),
|
||||
|
|
|
@ -74,7 +74,9 @@ class _ForgotPasswordDesktopViewState
|
|||
SizedBox(
|
||||
width: 400,
|
||||
child: Text(
|
||||
"${AppConfig.appName} does not store your password. Create new wallet or use a Stack backup file to restore your wallet.",
|
||||
"${AppConfig.appName} does not store your password. "
|
||||
"Create new wallet or use a ${AppConfig.prefix} "
|
||||
"backup file to restore your wallet.",
|
||||
textAlign: TextAlign.center,
|
||||
style: STextStyles.desktopTextSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
|
@ -87,7 +89,7 @@ class _ForgotPasswordDesktopViewState
|
|||
height: 48,
|
||||
),
|
||||
PrimaryButton(
|
||||
label: "Create new Stack",
|
||||
label: "Create new ${AppConfig.prefix}",
|
||||
onPressed: () {
|
||||
const shouldCreateNew = true;
|
||||
Navigator.of(context).pushNamed(
|
||||
|
@ -100,7 +102,7 @@ class _ForgotPasswordDesktopViewState
|
|||
height: 24,
|
||||
),
|
||||
SecondaryButton(
|
||||
label: "Restore from Stack backup",
|
||||
label: "Restore from ${AppConfig.prefix} backup",
|
||||
onPressed: () {
|
||||
const shouldCreateNew = false;
|
||||
Navigator.of(context).pushNamed(
|
||||
|
|
|
@ -94,7 +94,7 @@ class _ForgottenPassphraseRestoreFromSWBState
|
|||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
"Decrypting ${AppConfig.prefix} backup file",
|
||||
style: STextStyles.pageTitleH2(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textWhite,
|
||||
|
@ -245,7 +245,7 @@ class _ForgottenPassphraseRestoreFromSWBState
|
|||
height: 32,
|
||||
),
|
||||
Text(
|
||||
"Use your Stack wallet backup file to restore your wallets, address book, and wallet preferences.",
|
||||
"Use your ${AppConfig.prefix} backup file to restore your wallets, address book, and wallet preferences.",
|
||||
textAlign: TextAlign.center,
|
||||
style: STextStyles.desktopTextSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../pages/settings_views/global_settings_view/advanced_views/manage_coin_units/manage_coin_units_view.dart';
|
||||
import '../../../../providers/global/prefs_provider.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -161,6 +162,47 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
|
|||
],
|
||||
),
|
||||
),
|
||||
// showExchange pref.
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Divider(
|
||||
thickness: 0.5,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Enable exchange features",
|
||||
style: STextStyles.desktopTextExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
width: 40,
|
||||
child: DraggableSwitchButton(
|
||||
isOn: ref.watch(
|
||||
prefsChangeNotifierProvider.select(
|
||||
(value) => value.enableExchange,
|
||||
),
|
||||
),
|
||||
onValueChanged: (newValue) {
|
||||
ref
|
||||
.read(prefsChangeNotifierProvider)
|
||||
.enableExchange = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Divider(
|
||||
|
@ -184,7 +226,7 @@ class _AdvancedSettings extends ConsumerState<AdvancedSettings> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Stack Experience",
|
||||
"${AppConfig.prefix} Experience",
|
||||
style: STextStyles.desktopTextExtraSmall(
|
||||
context,
|
||||
).copyWith(
|
||||
|
|
|
@ -226,9 +226,9 @@ class _DesktopEditBlockExplorerDialogState
|
|||
" every block explorer has a slightly different URL scheme."
|
||||
"\n\n"
|
||||
"Paste in your block explorer of choice, then edit in"
|
||||
" [TXID] where the transaction ID should go, and Stack"
|
||||
" Wallet will auto fill the transaction ID in that place"
|
||||
" of the URL.",
|
||||
" [TXID] where the transaction ID should go, and "
|
||||
"${AppConfig.appName} will auto fill the transaction"
|
||||
" ID in that place of the URL.",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../db/hive/db.dart';
|
||||
import '../../../../providers/global/prefs_provider.dart';
|
||||
import '../../../../providers/global/price_provider.dart';
|
||||
|
@ -69,7 +70,7 @@ class _StackPrivacyDialog extends ConsumerState<StackPrivacyDialog> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Text(
|
||||
"Choose Your Stack Experience",
|
||||
"Choose Your ${AppConfig.prefix} Experience",
|
||||
style: STextStyles.desktopH3(context),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
|
@ -192,9 +193,11 @@ class _StackPrivacyDialog extends ConsumerState<StackPrivacyDialog> {
|
|||
)
|
||||
.then((_) {
|
||||
if (isEasy) {
|
||||
unawaited(
|
||||
ExchangeDataLoadingService.instance.loadAll(),
|
||||
);
|
||||
if (AppConfig.hasFeature(AppFeature.swap)) {
|
||||
unawaited(
|
||||
ExchangeDataLoadingService.instance.loadAll(),
|
||||
);
|
||||
}
|
||||
ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.start(true);
|
||||
|
|
|
@ -20,6 +20,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
|
||||
import 'package:zxcvbn/zxcvbn.dart';
|
||||
|
||||
import '../../../../app_config.dart';
|
||||
import '../../../../notifications/show_flush_bar.dart';
|
||||
import '../../../../pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import '../../../../pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
|
||||
|
@ -776,7 +777,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
if (Platform.isAndroid) {
|
||||
return StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup enabled and saved to:",
|
||||
"${AppConfig.prefix} Auto Backup enabled and saved to:",
|
||||
message: fileToSave,
|
||||
);
|
||||
} else if (Util.isDesktop) {
|
||||
|
@ -800,7 +801,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Stack Auto Backup enabled!",
|
||||
"${AppConfig.prefix} Auto Backup enabled!",
|
||||
style:
|
||||
STextStyles.desktopH3(
|
||||
context,
|
||||
|
@ -834,7 +835,8 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
|
|||
);
|
||||
} else {
|
||||
return const StackOkDialog(
|
||||
title: "Stack Auto Backup enabled!",
|
||||
title:
|
||||
"${AppConfig.prefix} Auto Backup enabled!",
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../../app_config.dart';
|
||||
import '../../../pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart';
|
||||
import '../../../providers/global/prefs_provider.dart';
|
||||
import '../../../themes/stack_colors.dart';
|
||||
|
@ -115,7 +116,7 @@ class _SyncingPreferencesSettings
|
|||
),
|
||||
TextSpan(
|
||||
text:
|
||||
"\n\nSet up your syncing preferences for all wallets in your Stack.",
|
||||
"\n\nSet up your syncing preferences for all wallets in your ${AppConfig.prefix}.",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:event_bus/event_bus.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../models/isar/models/log.dart';
|
||||
import '../utilities/logger.dart';
|
||||
|
||||
|
@ -98,7 +99,7 @@ class DebugService extends ChangeNotifier {
|
|||
Future<String> exportToFile(String directory, EventBus eventBus) async {
|
||||
final now = DateTime.now();
|
||||
final filename =
|
||||
"Stack_Wallet_logs_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.txt";
|
||||
"${AppConfig.prefix}_Wallet_logs_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.txt";
|
||||
final filepath = "$directory/$filename";
|
||||
final File file = await File(filepath).create();
|
||||
|
||||
|
|
1
lib/services/event_bus/events/wallet_added_event.dart
Normal file
1
lib/services/event_bus/events/wallet_added_event.dart
Normal file
|
@ -0,0 +1 @@
|
|||
class WalletAddedEvent {}
|
|
@ -284,7 +284,13 @@ abstract class Frost {
|
|||
|
||||
static String createSignConfig({
|
||||
required int network,
|
||||
required List<({UTXO utxo, Uint8List scriptPubKey})> inputs,
|
||||
required List<
|
||||
({
|
||||
UTXO utxo,
|
||||
Uint8List scriptPubKey,
|
||||
AddressDerivationData addressDerivationData
|
||||
})>
|
||||
inputs,
|
||||
required List<({String address, Amount amount, bool isChange})> outputs,
|
||||
required String changeAddress,
|
||||
required int feePerWeight,
|
||||
|
@ -299,6 +305,7 @@ abstract class Frost {
|
|||
vout: e.utxo.vout,
|
||||
value: e.utxo.value,
|
||||
scriptPubKey: e.scriptPubKey,
|
||||
addressDerivationData: e.addressDerivationData,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
|
|
|
@ -8,13 +8,16 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
import '../models/notification_model.dart';
|
||||
import '../utilities/prefs.dart';
|
||||
import 'notifications_service.dart';
|
||||
|
||||
class NotificationApi {
|
||||
abstract final class NotificationApi {
|
||||
static Completer<void>? _initCalledCompleter;
|
||||
static final _notifications = FlutterLocalNotificationsPlugin();
|
||||
// static final onNotifications = BehaviorSubject<String?>();
|
||||
|
||||
|
@ -33,6 +36,16 @@ class NotificationApi {
|
|||
}
|
||||
|
||||
static Future<void> init({bool initScheduled = false}) async {
|
||||
if (_initCalledCompleter == null) {
|
||||
_initCalledCompleter = Completer<void>();
|
||||
} else {
|
||||
if (_initCalledCompleter!.isCompleted) {
|
||||
return;
|
||||
} else {
|
||||
return await _initCalledCompleter!.future;
|
||||
}
|
||||
}
|
||||
|
||||
const android = AndroidInitializationSettings('app_icon_alpha');
|
||||
const iOS = DarwinInitializationSettings();
|
||||
const linux = LinuxInitializationSettings(
|
||||
|
@ -54,12 +67,18 @@ class NotificationApi {
|
|||
// onNotifications.add(payload.payload);
|
||||
// },
|
||||
);
|
||||
_initCalledCompleter!.complete();
|
||||
}
|
||||
|
||||
static Future<void> clearNotifications() async => _notifications.cancelAll();
|
||||
static Future<void> clearNotifications() async {
|
||||
await init();
|
||||
await _notifications.cancelAll();
|
||||
}
|
||||
|
||||
static Future<void> clearNotification(int id) async =>
|
||||
_notifications.cancel(id);
|
||||
static Future<void> clearNotification(int id) async {
|
||||
await init();
|
||||
await _notifications.cancel(id);
|
||||
}
|
||||
|
||||
//===================================
|
||||
static late Prefs prefs;
|
||||
|
@ -79,6 +98,7 @@ class NotificationApi {
|
|||
String? changeNowId,
|
||||
String? payload,
|
||||
}) async {
|
||||
await init();
|
||||
await prefs.incrementCurrentNotificationIndex();
|
||||
final id = prefs.currentNotificationId;
|
||||
|
||||
|
|
|
@ -13,12 +13,10 @@ import 'dart:async';
|
|||
import 'package:flutter_libmonero/monero/monero.dart' as monero;
|
||||
import 'package:flutter_libmonero/wownero/wownero.dart' as wownero;
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../db/hive/db.dart';
|
||||
import '../db/isar/main_db.dart';
|
||||
import 'node_service.dart';
|
||||
import 'notifications_service.dart';
|
||||
import 'trade_sent_from_stack_service.dart';
|
||||
import '../app_config.dart';
|
||||
import '../utilities/enums/sync_type_enum.dart';
|
||||
import '../utilities/flutter_secure_storage_interface.dart';
|
||||
import '../utilities/logger.dart';
|
||||
|
@ -28,6 +26,11 @@ import '../wallets/isar/models/wallet_info.dart';
|
|||
import '../wallets/wallet/impl/epiccash_wallet.dart';
|
||||
import '../wallets/wallet/wallet.dart';
|
||||
import '../wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart';
|
||||
import 'event_bus/events/wallet_added_event.dart';
|
||||
import 'event_bus/global_event_bus.dart';
|
||||
import 'node_service.dart';
|
||||
import 'notifications_service.dart';
|
||||
import 'trade_sent_from_stack_service.dart';
|
||||
|
||||
class Wallets {
|
||||
Wallets._private();
|
||||
|
@ -59,6 +62,7 @@ class Wallets {
|
|||
);
|
||||
}
|
||||
_wallets[wallet.walletId] = wallet;
|
||||
GlobalEventBus.instance.fire(WalletAddedEvent());
|
||||
}
|
||||
|
||||
Future<void> deleteWallet(
|
||||
|
|
|
@ -31,7 +31,9 @@ final pThemeService = Provider<ThemeService>((ref) {
|
|||
});
|
||||
|
||||
class ThemeService {
|
||||
static const _currentDefaultThemeVersion = 10;
|
||||
// dumb quick conditional based on name. Should really be done better
|
||||
static const _currentDefaultThemeVersion =
|
||||
AppConfig.appName == "Campfire" ? 17 : 15;
|
||||
ThemeService._();
|
||||
static ThemeService? _instance;
|
||||
static ThemeService get instance => _instance ??= ThemeService._();
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
abstract class DefaultNodes {
|
||||
static const String defaultNodeIdPrefix = "default_";
|
||||
static String buildId(CryptoCurrency cryptoCurrency) =>
|
||||
"$defaultNodeIdPrefix${cryptoCurrency.identifier}";
|
||||
static const String defaultName = "Stack Default";
|
||||
static const String defaultName = "${AppConfig.prefix} Default";
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ class Prefs extends ChangeNotifier {
|
|||
_useTor = await _getUseTor();
|
||||
_fusionServerInfo = await _getFusionServerInfo();
|
||||
_autoPin = await _getAutoPin();
|
||||
_enableExchange = await _getEnableExchange();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
@ -1131,4 +1132,30 @@ class Prefs extends ChangeNotifier {
|
|||
) as bool? ??
|
||||
false;
|
||||
}
|
||||
|
||||
// Show or hide exchange (buy & swap) features.
|
||||
|
||||
bool _enableExchange = true;
|
||||
|
||||
bool get enableExchange => _enableExchange;
|
||||
|
||||
set enableExchange(bool showExchange) {
|
||||
if (_enableExchange != showExchange) {
|
||||
DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNamePrefs,
|
||||
key: "showExchange",
|
||||
value: showExchange,
|
||||
);
|
||||
_enableExchange = showExchange;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> _getEnableExchange() async {
|
||||
return await DB.instance.get<dynamic>(
|
||||
boxName: DB.boxNamePrefs,
|
||||
key: "showExchange",
|
||||
) as bool? ??
|
||||
true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,9 +127,21 @@ class BitcoinFrost extends FrostCurrency {
|
|||
);
|
||||
|
||||
@override
|
||||
String pubKeyToScriptHash({required Uint8List pubKey}) {
|
||||
Uint8List addressToPubkey({required String address}) {
|
||||
try {
|
||||
return Bip39HDCurrency.convertBytesToScriptHash(pubKey);
|
||||
final addr = coinlib.Address.fromString(address, networkParams);
|
||||
return addr.program.script.compiled;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String addressToScriptHash({required String address}) {
|
||||
try {
|
||||
return Bip39HDCurrency.convertBytesToScriptHash(
|
||||
addressToPubkey(address: address),
|
||||
);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ import '../crypto_currency.dart';
|
|||
abstract class FrostCurrency extends CryptoCurrency {
|
||||
FrostCurrency(super.network);
|
||||
|
||||
String pubKeyToScriptHash({required Uint8List pubKey});
|
||||
// String pubKeyToScriptHash({required Uint8List pubKey});
|
||||
|
||||
String addressToScriptHash({required String address});
|
||||
|
||||
Uint8List addressToPubkey({required String address});
|
||||
|
||||
Amount get dustLimit;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue