mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 09:47:37 +00:00
Merge pull request #120 from cypherstack/updatedesktop
add wownero and fix some building problems for linux
This commit is contained in:
commit
94c606f6c3
60 changed files with 3095 additions and 449 deletions
23
.github/workflows/test.yaml
vendored
23
.github/workflows/test.yaml
vendored
|
@ -54,12 +54,31 @@ jobs:
|
|||
Write-Output "::set-output name=SECRET_FILE_FIRO::$secretFileFiro";
|
||||
Write-Output "::set-output name=SECRET_FILE_FIRO_HASH::$($secretFileFiroHash.Hash)";
|
||||
Write-Output "Secret file $secretFileFiro has hash $($secretFileFiroHash.Hash)";
|
||||
|
||||
$secretFileBitcoinCash = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/bitcoincash/bitcoincash_wallet_test_parameters.dart";
|
||||
$encodedBytes = [System.Convert]::FromBase64String($env:BITCOINCASH_TEST);
|
||||
Set-Content $secretFileBitcoinCash -Value $encodedBytes -AsByteStream;
|
||||
$secretFileBitcoinCashHash = Get-FileHash $secretFileBitcoinCash;
|
||||
Write-Output "::set-output name=SECRET_FILE_BITCOINCASH::$secretFileBitcoinCash";
|
||||
Write-Output "::set-output name=SECRET_FILE_BITCOINCASH_HASH::$($secretFileBitcoinCashHash.Hash)";
|
||||
Write-Output "Secret file $secretFileBitcoinCash has hash $($secretFileBitcoinCashHash.Hash)";
|
||||
|
||||
$secretFileNamecoin = Join-Path -Path $env:GITHUB_WORKSPACE -ChildPath "test/services/coins/namecoin/namecoin_wallet_test_parameters.dart";
|
||||
$encodedBytes = [System.Convert]::FromBase64String($env:NAMECOIN_TEST);
|
||||
Set-Content $secretFileNamecoin -Value $encodedBytes -AsByteStream;
|
||||
$secretFileNamecoinHash = Get-FileHash $secretFileNamecoin;
|
||||
Write-Output "::set-output name=SECRET_FILE_NAMECOIN::$secretFileNamecoin";
|
||||
Write-Output "::set-output name=SECRET_FILE_NAMECOIN_HASH::$($secretFileNamecoinHash.Hash)";
|
||||
Write-Output "Secret file $secretFileNamecoin has hash $($secretFileNamecoinHash.Hash)";
|
||||
|
||||
shell: pwsh
|
||||
env:
|
||||
CHANGE_NOW: ${{ secrets.CHANGE_NOW }}
|
||||
BITCOIN_TEST: ${{ secrets.BITCOIN_TEST }}
|
||||
DOGECOIN_TEST: ${{ secrets.DOGECOIN_TEST }}
|
||||
FIRO_TEST: ${{ secrets.FIRO_TEST }}
|
||||
BITCOINCASH_TEST: ${{ secrets.BITCOINCASH_TEST }}
|
||||
NAMECOIN_TEST: ${{ secrets.NAMECOIN_TEST }}
|
||||
# - name: Analyze
|
||||
# run: flutter analyze
|
||||
- name: Test
|
||||
|
@ -76,6 +95,8 @@ jobs:
|
|||
Remove-Item -Path $env:BITCOIN_TEST;
|
||||
Remove-Item -Path $env:DOGECOIN_TEST;
|
||||
Remove-Item -Path $env:FIRO_TEST;
|
||||
Remove-Item -Path $env:BITCOINCASH_TEST;
|
||||
Remove-Item -Path $env:NAMECOIN_TEST;
|
||||
shell: pwsh
|
||||
if: always()
|
||||
env:
|
||||
|
@ -83,3 +104,5 @@ jobs:
|
|||
BITCOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_BITCOIN }}
|
||||
DOGECOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_DOGECOIN }}
|
||||
FIRO_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_FIRO }}
|
||||
BITCOINCASH_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_BITCOINCASH }}
|
||||
NAMECOIN_TEST: ${{ steps.secret-file1.outputs.SECRET_FILE_NAMECOIN }}
|
||||
|
|
25
README.md
25
README.md
|
@ -32,7 +32,10 @@ cd stack_wallet
|
|||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
You will need to install all dependencies listed in each of the plugins in the crypto_plugins folder. (eg. [Monero](https://github.com/cypherstack/flutter_libmonero), [Epic Cash](https://github.com/cypherstack/flutter_libepiccash) ) as of Sep 8th 2022 that is:
|
||||
Install all dependencies listed in each of the plugins in the crypto_plugins folder (eg. [flutter_libmonero](https://github.com/cypherstack/flutter_libmonero/blob/main/howto-build-android.md), [flutter_libepiccash](https://github.com/cypherstack/flutter_libepiccash) ) as of Oct 3rd 2022 that is:
|
||||
```
|
||||
sudo apt-get install unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm sudo apt-get install debhelper libclang-dev cargo rustc opencl-headers libssl-dev ocl-icd-opencl-dev
|
||||
```
|
||||
|
||||
Install [Rust](https://www.rust-lang.org/tools/install)
|
||||
```
|
||||
|
@ -45,6 +48,26 @@ sudo apt install build-essential debhelper cmake libclang-dev libncurses5-dev cl
|
|||
sudo apt install unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless
|
||||
```
|
||||
|
||||
Run prebuild script
|
||||
|
||||
```
|
||||
cd scripts
|
||||
./prebuild.sh
|
||||
// when finished go back to the root directory
|
||||
cd ..
|
||||
```
|
||||
|
||||
|
||||
Remove pre-installed system libraries for the following packages built by cryptography plugins in the crypto_plugins folder: `boost iconv libjson-dev libsecret openssl sodium unbound zmq`. You can use
|
||||
```
|
||||
sudo apt list --installed | grep boost
|
||||
```
|
||||
for example to find which pre-installed packages you may need to remove with `sudo apt remove`. Be careful, as some packages (especially boost) are linked to GNOME (GUI) packages: when in doubt, remove `-dev` packages first like with
|
||||
```
|
||||
sudo apt-get remove '^libboost.*-dev.*'
|
||||
```
|
||||
<!-- TODO: configure compiler to prefer built over system libraries -->
|
||||
|
||||
Building plugins for Android
|
||||
```
|
||||
cd scripts/android/
|
||||
|
|
7
assets/svg/coin_icons/Wownero.svg
Normal file
7
assets/svg/coin_icons/Wownero.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 23.8456C18.5421 23.8456 23.8456 18.5421 23.8456 12C23.8456 5.45778 18.5421 0.154297 12 0.154297C5.45778 0.154297 0.154297 5.45778 0.154297 12C0.154297 18.5421 5.45778 23.8456 12 23.8456Z" fill="white"/>
|
||||
<path d="M12 24C5.38264 24 0 18.6174 0 12C0 5.38264 5.38264 0 12 0C18.6174 0 24 5.38264 24 12C24 18.6174 18.6174 24 12 24ZM12 0.327974C5.55627 0.327974 0.327974 5.57556 0.327974 12C0.327974 18.4244 5.57556 23.672 12 23.672C18.4244 23.672 23.672 18.4244 23.672 12C23.672 5.57556 18.4437 0.327974 12 0.327974Z" fill="#D0509D"/>
|
||||
<path d="M11.9995 0.154297C6.75195 0.154297 2.29536 3.58838 0.751953 8.31507H6.73266V15.7427L11.961 10.0707H12.0381L17.2664 15.762V8.31507H23.2471C21.7037 3.58838 17.2471 0.154297 11.9995 0.154297Z" fill="#FFCD05"/>
|
||||
<path d="M6.56007 16.1672V8.48875H0.521484L0.598655 8.27653C2.19994 3.31833 6.79158 0 12.0006 0C17.2096 0 21.8012 3.31833 23.4025 8.27653L23.4797 8.48875H17.4411V16.1672L12.0006 10.283L6.56007 16.1672ZM11.9041 9.9164H12.1163L17.1131 15.3376V8.16077H23.036C21.3961 3.47267 16.9974 0.327974 12.0006 0.327974C7.0038 0.327974 2.60509 3.47267 0.965214 8.16077H6.88804V15.3376L11.9041 9.9164Z" fill="#D0509D"/>
|
||||
<path d="M18.2473 18.2508L16.5495 16.418L11.9965 11.4791L7.4434 16.418L5.74565 18.2508V15.762V9.5498H1.59774C1.40482 10.3601 1.28906 11.209 1.28906 12.0964C1.28906 18 6.07363 22.8038 11.9965 22.8038C17.9193 22.8038 22.7039 18.0193 22.7039 12.0964C22.7039 11.2283 22.5881 10.3794 22.3952 9.5498H18.228V15.762V18.2508H18.2473Z" fill="#D0519D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit 4bffa40cb60ad3d98cf0ea5b5d819f3f4895dcd6
|
||||
Subproject commit af5740e590cbd4d7a7c86592986288d9d568e23b
|
|
@ -449,7 +449,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 63;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -503,7 +503,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.39;
|
||||
MARKETING_VERSION = 1.4.48;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -633,7 +633,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 63;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -687,7 +687,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.39;
|
||||
MARKETING_VERSION = 1.4.48;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -709,7 +709,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 51;
|
||||
CURRENT_PROJECT_VERSION = 63;
|
||||
DEVELOPMENT_TEAM = 4DQKUWSG6C;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -763,7 +763,7 @@
|
|||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libmonero/cw_shared_external/ios/External/ios/**",
|
||||
"$(PROJECT_DIR)/../crypto_plugins/flutter_libepiccash/ios/libs",
|
||||
);
|
||||
MARKETING_VERSION = 1.4.39;
|
||||
MARKETING_VERSION = 1.4.48;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cypherstack.stackwallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:cw_core/wallet_type.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_libmonero/monero/monero.dart';
|
||||
import 'package:flutter_libmonero/wownero/wownero.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
|
@ -83,8 +84,11 @@ void main() async {
|
|||
if (Platform.isIOS) {
|
||||
appDirectory = (await getLibraryDirectory());
|
||||
}
|
||||
if (Platform.isLinux || Logging.isArmLinux) {
|
||||
appDirectory = Directory("${appDirectory.path}/.stackwallet");
|
||||
await appDirectory.create();
|
||||
}
|
||||
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
await Hive.initFlutter(appDirectory.path);
|
||||
if (!(Logging.isArmLinux || Logging.isTestEnv)) {
|
||||
final isar = await Isar.open(
|
||||
[LogSchema],
|
||||
|
@ -128,11 +132,14 @@ void main() async {
|
|||
|
||||
Hive.registerAdapter(NodeAdapter());
|
||||
|
||||
Hive.registerAdapter(WalletInfoAdapter());
|
||||
if (!Hive.isAdapterRegistered(WalletInfoAdapter().typeId)) {
|
||||
Hive.registerAdapter(WalletInfoAdapter());
|
||||
}
|
||||
|
||||
Hive.registerAdapter(WalletTypeAdapter());
|
||||
|
||||
Hive.registerAdapter(UnspentCoinsInfoAdapter());
|
||||
await Hive.initFlutter(appDirectory.path);
|
||||
|
||||
await Hive.openBox<dynamic>(DB.boxNameDBInfo);
|
||||
int dbVersion = DB.instance.get<dynamic>(
|
||||
|
@ -143,6 +150,7 @@ void main() async {
|
|||
}
|
||||
|
||||
monero.onStartup();
|
||||
wownero.onStartup();
|
||||
|
||||
await Hive.openBox<dynamic>(DB.boxNameTheme);
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
|
||||
final _numberOfPhraseWords = coin == Coin.monero
|
||||
? Constants.seedPhraseWordCountMonero
|
||||
: Constants.seedPhraseWordCountBip39;
|
||||
: coin == Coin.wownero
|
||||
? 14
|
||||
: Constants.seedPhraseWordCountBip39;
|
||||
|
||||
return MasterScaffold(
|
||||
isDesktop: isDesktop,
|
||||
|
|
|
@ -42,6 +42,11 @@ class _RestoreFromDatePickerState extends State<RestoreFromDatePicker> {
|
|||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Restore from...",
|
||||
hintStyle: STextStyles.fieldLabel(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultSearchIconLeft,
|
||||
),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
|
@ -200,7 +200,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
|
||||
// TODO: do actual check to make sure it is a valid mnemonic for monero
|
||||
if (bip39.validateMnemonic(mnemonic) == false &&
|
||||
!(widget.coin == Coin.monero)) {
|
||||
!(widget.coin == Coin.monero || widget.coin == Coin.wownero)) {
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Invalid seed phrase!",
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart';
|
||||
import 'package:stackwallet/pages/send_view/send_view.dart';
|
||||
import 'package:stackwallet/providers/exchange/exchange_flow_is_active_state_provider.dart';
|
||||
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
|
||||
|
@ -19,6 +20,9 @@ import 'package:stackwallet/widgets/rounded_container.dart';
|
|||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
final exchangeFromAddressBookAddressStateProvider =
|
||||
StateProvider<String>((ref) => "");
|
||||
|
||||
class ContactPopUp extends ConsumerWidget {
|
||||
const ContactPopUp({
|
||||
Key? key,
|
||||
|
@ -280,6 +284,45 @@ class ContactPopUp extends ConsumerWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (isExchangeFlow)
|
||||
const SizedBox(
|
||||
width: 6,
|
||||
),
|
||||
if (isExchangeFlow)
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
ref
|
||||
.read(
|
||||
exchangeFromAddressBookAddressStateProvider
|
||||
.state)
|
||||
.state = e.address;
|
||||
Navigator.of(context).popUntil(
|
||||
ModalRoute.withName(
|
||||
Step2View.routeName));
|
||||
},
|
||||
child: RoundedContainer(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
padding:
|
||||
const EdgeInsets.all(6),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.chevronRight,
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: Theme.of(context)
|
||||
.extension<
|
||||
StackColors>()!
|
||||
.accentColorDark),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (contact.id != "default" &&
|
||||
hasActiveWallet &&
|
||||
!isExchangeFlow)
|
||||
|
|
128
lib/pages/exchange_view/choose_from_stack_view.dart
Normal file
128
lib/pages/exchange_view/choose_from_stack_view.dart
Normal file
|
@ -0,0 +1,128 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_balance_future.dart';
|
||||
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
|
||||
|
||||
class ChooseFromStackView extends ConsumerStatefulWidget {
|
||||
const ChooseFromStackView({
|
||||
Key? key,
|
||||
required this.coin,
|
||||
}) : super(key: key);
|
||||
|
||||
final Coin coin;
|
||||
|
||||
static const String routeName = "/chooseFromStack";
|
||||
|
||||
@override
|
||||
ConsumerState<ChooseFromStackView> createState() =>
|
||||
_ChooseFromStackViewState();
|
||||
}
|
||||
|
||||
class _ChooseFromStackViewState extends ConsumerState<ChooseFromStackView> {
|
||||
late final Coin coin;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
coin = widget.coin;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletIds = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getWalletIdsFor(coin: coin)));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: const AppBarBackButton(),
|
||||
title: Text(
|
||||
"Choose your ${coin.ticker.toUpperCase()} wallet",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: walletIds.isEmpty
|
||||
? Column(
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"No ${coin.ticker.toUpperCase()} wallets",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: walletIds.length,
|
||||
itemBuilder: (context, index) {
|
||||
final manager = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(walletIds[index])));
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||
child: RawMaterialButton(
|
||||
splashColor:
|
||||
Theme.of(context).extension<StackColors>()!.highlight,
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.all(0),
|
||||
// color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
elevation: 0,
|
||||
onPressed: () async {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop(manager.walletId);
|
||||
}
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
// color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
WalletInfoCoinIcon(coin: coin),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
manager.walletName,
|
||||
style: STextStyles.titleBold12(context),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
WalletInfoRowBalanceFuture(
|
||||
walletId: walletIds[index],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -328,7 +328,12 @@ class _ConfirmChangeNowSendViewState
|
|||
children: [
|
||||
Text(
|
||||
"Total amount",
|
||||
style: STextStyles.titleBold12(context),
|
||||
style:
|
||||
STextStyles.titleBold12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textConfirmTotalAmount,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshiAmountToPrettyString(
|
||||
|
@ -342,7 +347,12 @@ class _ConfirmChangeNowSendViewState
|
|||
managerProvider
|
||||
.select((value) => value.coin),
|
||||
).ticker}",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textConfirmTotalAmount,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -3,6 +3,8 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/contact_popup.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
|
||||
import 'package:stackwallet/providers/exchange/exchange_flow_is_active_state_provider.dart';
|
||||
|
@ -17,6 +19,7 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
|
||||
|
@ -54,6 +57,15 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
late final FocusNode _toFocusNode;
|
||||
late final FocusNode _refundFocusNode;
|
||||
|
||||
bool isStackCoin(String ticker) {
|
||||
try {
|
||||
coinFromTickerCaseInsensitive(ticker);
|
||||
return true;
|
||||
} on ArgumentError catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
model = widget.model;
|
||||
|
@ -74,7 +86,22 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(tuple.item1)
|
||||
.currentReceivingAddress
|
||||
.then((value) => _toController.text = value);
|
||||
.then((value) {
|
||||
_toController.text = value;
|
||||
model.recipientAddress = _toController.text;
|
||||
});
|
||||
} else {
|
||||
if (model.sendTicker.toUpperCase() ==
|
||||
tuple.item2.ticker.toUpperCase()) {
|
||||
ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(tuple.item1)
|
||||
.currentReceivingAddress
|
||||
.then((value) {
|
||||
_refundController.text = value;
|
||||
model.refundAddress = _refundController.text;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,15 +185,36 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
"Recipient Wallet",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// // TODO: choose from stack?
|
||||
// },
|
||||
// child: Text(
|
||||
// "Choose from Stack",
|
||||
// style: STextStyles.link2(context),
|
||||
// ),
|
||||
// ),
|
||||
if (isStackCoin(model.receiveTicker))
|
||||
BlueTextButton(
|
||||
text: "Choose from stack",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = coinFromTickerCaseInsensitive(
|
||||
model.receiveTicker,
|
||||
);
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
ChooseFromStackView.routeName,
|
||||
arguments: coin,
|
||||
)
|
||||
.then((value) async {
|
||||
if (value is String) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(value);
|
||||
|
||||
_toController.text = manager.walletName;
|
||||
model.recipientAddress = await manager
|
||||
.currentReceivingAddress;
|
||||
}
|
||||
});
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Info);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -195,6 +243,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
),
|
||||
focusNode: _toFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
decoration: standardInputDecoration(
|
||||
"Enter the ${model.receiveTicker.toUpperCase()} payout address",
|
||||
_toFocusNode,
|
||||
|
@ -221,6 +272,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
"sendViewClearAddressFieldButtonKey"),
|
||||
onTap: () {
|
||||
_toController.text = "";
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
|
@ -239,6 +292,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
data.text!.trim();
|
||||
|
||||
_toController.text = content;
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -259,13 +314,31 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.state = true;
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
AddressBookView.routeName,
|
||||
)
|
||||
.then((_) => ref
|
||||
AddressBookView.routeName,
|
||||
)
|
||||
.then((_) {
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = false;
|
||||
|
||||
final address = ref
|
||||
.read(
|
||||
exchangeFromAddressBookAddressStateProvider
|
||||
.state)
|
||||
.state;
|
||||
if (address.isNotEmpty) {
|
||||
_toController.text = address;
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
exchangeFromAddressBookAddressStateProvider
|
||||
.state)
|
||||
.state = false);
|
||||
.state = "";
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const AddressBookIcon(),
|
||||
),
|
||||
|
@ -299,11 +372,15 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
// auto fill address
|
||||
_toController.text =
|
||||
results["address"] ?? "";
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
} else {
|
||||
_toController.text =
|
||||
qrResult.rawContent;
|
||||
model.recipientAddress =
|
||||
_toController.text;
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -348,15 +425,37 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
"Refund Wallet (required)",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// // TODO: choose from stack?
|
||||
// },
|
||||
// child: Text(
|
||||
// "Choose from Stack",
|
||||
// style: STextStyles.link2(context),
|
||||
// ),
|
||||
// ),
|
||||
if (isStackCoin(model.sendTicker))
|
||||
BlueTextButton(
|
||||
text: "Choose from stack",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin = coinFromTickerCaseInsensitive(
|
||||
model.sendTicker,
|
||||
);
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
ChooseFromStackView.routeName,
|
||||
arguments: coin,
|
||||
)
|
||||
.then((value) async {
|
||||
if (value is String) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(value);
|
||||
|
||||
_refundController.text =
|
||||
manager.walletName;
|
||||
model.refundAddress = await manager
|
||||
.currentReceivingAddress;
|
||||
}
|
||||
});
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Info);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -384,6 +483,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
),
|
||||
focusNode: _refundFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
decoration: standardInputDecoration(
|
||||
"Enter ${model.sendTicker.toUpperCase()} refund address",
|
||||
_refundFocusNode,
|
||||
|
@ -410,6 +512,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
"sendViewClearAddressFieldButtonKey"),
|
||||
onTap: () {
|
||||
_refundController.text = "";
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
|
@ -429,6 +533,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
|
||||
_refundController.text =
|
||||
content;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -450,13 +556,26 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.state = true;
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
AddressBookView.routeName,
|
||||
)
|
||||
.then((_) => ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = false);
|
||||
AddressBookView.routeName,
|
||||
)
|
||||
.then((_) {
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = false;
|
||||
final address = ref
|
||||
.read(
|
||||
exchangeFromAddressBookAddressStateProvider
|
||||
.state)
|
||||
.state;
|
||||
if (address.isNotEmpty) {
|
||||
_refundController.text =
|
||||
address;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: const AddressBookIcon(),
|
||||
),
|
||||
|
@ -490,11 +609,15 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
// auto fill address
|
||||
_refundController.text =
|
||||
results["address"] ?? "";
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
} else {
|
||||
_refundController.text =
|
||||
qrResult.rawContent;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
@ -556,9 +679,6 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
model.recipientAddress = _toController.text;
|
||||
model.refundAddress = _refundController.text;
|
||||
|
||||
Navigator.of(context).pushNamed(
|
||||
Step3View.routeName,
|
||||
arguments: model);
|
||||
|
|
|
@ -16,6 +16,7 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
||||
|
@ -222,6 +223,26 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
Expanded(
|
||||
child: TextButton(
|
||||
onPressed: () async {
|
||||
unawaited(
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async => false,
|
||||
child: Container(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.overlay
|
||||
.withOpacity(0.6),
|
||||
child: const CustomLoadingOverlay(
|
||||
message: "Creating a trade",
|
||||
eventBus: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
ChangeNowResponse<ExchangeTransaction>
|
||||
response;
|
||||
if (model.rateType ==
|
||||
|
@ -251,6 +272,10 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
}
|
||||
|
||||
if (response.value == null) {
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
unawaited(showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
|
@ -273,8 +298,6 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
.getTransactionStatus(
|
||||
id: response.value!.id);
|
||||
|
||||
debugPrint("WTF: $statusResponse");
|
||||
|
||||
String status = "Waiting";
|
||||
if (statusResponse.value != null) {
|
||||
status = statusResponse.value!.status.name;
|
||||
|
@ -290,6 +313,10 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
status += " for deposit";
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
unawaited(NotificationApi.showNotification(
|
||||
changeNowId: model.trade!.id,
|
||||
title: status,
|
||||
|
|
|
@ -232,7 +232,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
? ref.read(estimatedRateExchangeFormProvider).fromAmountString
|
||||
: ref.read(fixedRateExchangeFormProvider).fromAmountString;
|
||||
_receiveController.text = isEstimated
|
||||
? ref.read(estimatedRateExchangeFormProvider).toAmountString
|
||||
? "-" //ref.read(estimatedRateExchangeFormProvider).toAmountString
|
||||
: ref.read(fixedRateExchangeFormProvider).toAmountString;
|
||||
|
||||
_sendFocusNode.addListener(() async {
|
||||
|
@ -260,7 +260,11 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
.read(fixedRateExchangeFormProvider)
|
||||
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
|
||||
}
|
||||
_receiveController.text = "";
|
||||
_receiveController.text =
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType ==
|
||||
ExchangeRateType.estimated
|
||||
? "-"
|
||||
: "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -325,7 +329,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
: fixedRateExchangeFormProvider.select(
|
||||
(value) => value.toAmountString), (previous, String next) {
|
||||
if (!_receiveFocusNode.hasFocus) {
|
||||
_receiveController.text = next;
|
||||
_receiveController.text = isEstimated && next.isEmpty ? "-" : next;
|
||||
debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED");
|
||||
if (_swapLock) {
|
||||
_sendController.text = isEstimated
|
||||
|
@ -345,7 +349,12 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
debugPrint("SEND AMOUNT LISTENER ACTIVATED");
|
||||
if (_swapLock) {
|
||||
_receiveController.text = isEstimated
|
||||
? ref.read(estimatedRateExchangeFormProvider).toAmountString
|
||||
? ref
|
||||
.read(estimatedRateExchangeFormProvider)
|
||||
.toAmountString
|
||||
.isEmpty
|
||||
? "-"
|
||||
: ref.read(estimatedRateExchangeFormProvider).toAmountString
|
||||
: ref.read(fixedRateExchangeFormProvider).toAmountString;
|
||||
}
|
||||
}
|
||||
|
@ -424,7 +433,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
.setFromAmountAndCalculateToAmount(
|
||||
Decimal.zero, false);
|
||||
}
|
||||
_receiveController.text = "";
|
||||
_receiveController.text = isEstimated ? "-" : "";
|
||||
}
|
||||
},
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
|
@ -737,7 +746,7 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
|
|||
.exchangeRateType ==
|
||||
ExchangeRateType.estimated,
|
||||
onTap: () {
|
||||
if (_receiveController.text == "-") {
|
||||
if (!isEstimated && _receiveController.text == "-") {
|
||||
_receiveController.text = "";
|
||||
}
|
||||
},
|
||||
|
|
|
@ -48,6 +48,26 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
late final String address;
|
||||
late final ExchangeTransaction trade;
|
||||
|
||||
String formatAmount(Decimal amount, Coin coin) {
|
||||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.dogecoin:
|
||||
case Coin.epicCash:
|
||||
case Coin.firo:
|
||||
case Coin.namecoin:
|
||||
case Coin.bitcoinTestNet:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.dogecoinTestNet:
|
||||
case Coin.firoTestNet:
|
||||
return amount.toStringAsFixed(Constants.decimalPlaces);
|
||||
case Coin.monero:
|
||||
return amount.toStringAsFixed(Constants.decimalPlacesMonero);
|
||||
case Coin.wownero:
|
||||
return amount.toStringAsFixed(Constants.decimalPlacesWownero);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
coin = widget.coin;
|
||||
|
@ -59,6 +79,11 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
final walletIds = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getWalletIdsFor(coin: coin)));
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
|
@ -68,44 +93,41 @@ class _SendFromViewState extends ConsumerState<SendFromView> {
|
|||
},
|
||||
),
|
||||
title: Text(
|
||||
"Send ",
|
||||
"Send from",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Wrap(
|
||||
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Choose your ${coin.ticker} wallet",
|
||||
style: STextStyles.pageTitleH1(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"You need to send ${amount.toStringAsFixed(coin == Coin.monero ? 12 : 8)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"You need to send ${formatAmount(amount, coin)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
...ref
|
||||
.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.managers))
|
||||
.where((element) => element.coin == coin)
|
||||
.map((e) => SendFromCard(
|
||||
walletId: e.walletId,
|
||||
amount: amount,
|
||||
address: address,
|
||||
trade: trade,
|
||||
))
|
||||
.toList(growable: false)
|
||||
],
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: walletIds.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: SendFromCard(
|
||||
walletId: walletIds[index],
|
||||
amount: amount,
|
||||
address: address,
|
||||
trade: trade,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -163,7 +185,7 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
child: MaterialButton(
|
||||
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||
key: Key("walletsSheetItemButtonKey_$walletId"),
|
||||
padding: const EdgeInsets.all(5),
|
||||
padding: const EdgeInsets.all(8),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
|
@ -276,55 +298,61 @@ class _SendFromCardState extends ConsumerState<SendFromCard> {
|
|||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.iconFor(coin: coin),
|
||||
width: 20,
|
||||
height: 20,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
manager.walletName,
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: manager.totalBalance,
|
||||
builder: (builderContext, AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: snapshot.data!,
|
||||
locale: locale,
|
||||
decimalPlaces: coin == Coin.monero ? 12 : 8,
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: const [
|
||||
"Loading balance",
|
||||
"Loading balance.",
|
||||
"Loading balance..",
|
||||
"Loading balance..."
|
||||
],
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
manager.walletName,
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
FutureBuilder(
|
||||
future: manager.totalBalance,
|
||||
builder: (builderContext, AsyncSnapshot<Decimal> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return Text(
|
||||
"${Format.localizedStringAsFixed(
|
||||
value: snapshot.data!,
|
||||
locale: locale,
|
||||
decimalPlaces: coin == Coin.monero
|
||||
? Constants.decimalPlacesMonero
|
||||
: coin == Coin.wownero
|
||||
? Constants.decimalPlacesWownero
|
||||
: Constants.decimalPlaces,
|
||||
)} ${coin.ticker}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: const [
|
||||
"Loading balance",
|
||||
"Loading balance.",
|
||||
"Loading balance..",
|
||||
"Loading balance..."
|
||||
],
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:stackwallet/models/exchange/change_now/exchange_transaction_stat
|
|||
import 'package:stackwallet/models/paymint/transactions_model.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/edit_note_view.dart';
|
||||
import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_details_view.dart';
|
||||
import 'package:stackwallet/providers/exchange/change_now_provider.dart';
|
||||
|
@ -24,6 +25,7 @@ import 'package:stackwallet/utilities/format.dart';
|
|||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
@ -60,6 +62,15 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
|
||||
String _note = "";
|
||||
|
||||
bool isStackCoin(String ticker) {
|
||||
try {
|
||||
coinFromTickerCaseInsensitive(ticker);
|
||||
return true;
|
||||
} on ArgumentError catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
initState() {
|
||||
tradeId = widget.tradeId;
|
||||
|
@ -345,9 +356,48 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Send ${trade.fromCurrency.toUpperCase()} to this address",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Send ${trade.fromCurrency.toUpperCase()} to this address",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final address = trade.payinAddress;
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: address,
|
||||
),
|
||||
);
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
context: context,
|
||||
));
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.copy,
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.infoItemIcons,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4,
|
||||
),
|
||||
Text(
|
||||
"Copy",
|
||||
style: STextStyles.link2(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
|
@ -717,6 +767,32 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (isStackCoin(trade.fromCurrency) &&
|
||||
trade.statusObject != null &&
|
||||
(trade.statusObject!.status ==
|
||||
ChangeNowTransactionStatus.New ||
|
||||
trade.statusObject!.status ==
|
||||
ChangeNowTransactionStatus.Waiting))
|
||||
SecondaryButton(
|
||||
label: "Send from Stack",
|
||||
onPressed: () {
|
||||
final amount = sendAmount;
|
||||
final address = trade.payinAddress;
|
||||
|
||||
final coin =
|
||||
coinFromTickerCaseInsensitive(trade.fromCurrency);
|
||||
|
||||
Navigator.of(context).pushNamed(
|
||||
SendFromView.routeName,
|
||||
arguments: Tuple4(
|
||||
coin,
|
||||
amount,
|
||||
address,
|
||||
trade,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -83,8 +83,8 @@ class _WalletInitiatedExchangeViewState
|
|||
child: Container(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark
|
||||
.withOpacity(0.8),
|
||||
.overlay
|
||||
.withOpacity(0.6),
|
||||
child: const CustomLoadingOverlay(
|
||||
message: "Updating exchange rate",
|
||||
eventBus: null,
|
||||
|
@ -271,7 +271,11 @@ class _WalletInitiatedExchangeViewState
|
|||
.read(fixedRateExchangeFormProvider)
|
||||
.setFromAmountAndCalculateToAmount(Decimal.zero, true);
|
||||
}
|
||||
_receiveController.text = "";
|
||||
_receiveController.text =
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType ==
|
||||
ExchangeRateType.estimated
|
||||
? "-"
|
||||
: "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -329,7 +333,7 @@ class _WalletInitiatedExchangeViewState
|
|||
: fixedRateExchangeFormProvider.select(
|
||||
(value) => value.toAmountString), (previous, String next) {
|
||||
if (!_receiveFocusNode.hasFocus) {
|
||||
_receiveController.text = next;
|
||||
_receiveController.text = isEstimated && next.isEmpty ? "-" : next;
|
||||
debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED");
|
||||
if (_swapLock) {
|
||||
_sendController.text = isEstimated
|
||||
|
@ -349,7 +353,12 @@ class _WalletInitiatedExchangeViewState
|
|||
debugPrint("SEND AMOUNT LISTENER ACTIVATED");
|
||||
if (_swapLock) {
|
||||
_receiveController.text = isEstimated
|
||||
? ref.read(estimatedRateExchangeFormProvider).toAmountString
|
||||
? ref
|
||||
.read(estimatedRateExchangeFormProvider)
|
||||
.toAmountString
|
||||
.isEmpty
|
||||
? "-"
|
||||
: ref.read(estimatedRateExchangeFormProvider).toAmountString
|
||||
: ref.read(fixedRateExchangeFormProvider).toAmountString;
|
||||
}
|
||||
}
|
||||
|
@ -469,7 +478,7 @@ class _WalletInitiatedExchangeViewState
|
|||
.setFromAmountAndCalculateToAmount(
|
||||
Decimal.zero, false);
|
||||
}
|
||||
_receiveController.text = "";
|
||||
_receiveController.text = isEstimated ? "-" : "";
|
||||
}
|
||||
},
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
|
@ -808,7 +817,8 @@ class _WalletInitiatedExchangeViewState
|
|||
.exchangeRateType ==
|
||||
ExchangeRateType.estimated,
|
||||
onTap: () {
|
||||
if (_receiveController.text == "-") {
|
||||
if (!isEstimated &&
|
||||
_receiveController.text == "-") {
|
||||
_receiveController.text = "";
|
||||
}
|
||||
},
|
||||
|
@ -1280,23 +1290,23 @@ class _WalletInitiatedExchangeViewState
|
|||
.exchangeRateType ==
|
||||
ExchangeRateType.estimated;
|
||||
|
||||
final ft = isEstimated
|
||||
? ref
|
||||
.read(
|
||||
estimatedRateExchangeFormProvider)
|
||||
.from
|
||||
?.ticker ??
|
||||
""
|
||||
: ref
|
||||
.read(
|
||||
fixedRateExchangeFormProvider)
|
||||
.market
|
||||
?.from ??
|
||||
"";
|
||||
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(walletId);
|
||||
// final ft = isEstimated
|
||||
// ? ref
|
||||
// .read(
|
||||
// estimatedRateExchangeFormProvider)
|
||||
// .from
|
||||
// ?.ticker ??
|
||||
// ""
|
||||
// : ref
|
||||
// .read(
|
||||
// fixedRateExchangeFormProvider)
|
||||
// .market
|
||||
// ?.from ??
|
||||
// "";
|
||||
//
|
||||
// final manager = ref
|
||||
// .read(walletsChangeNotifierProvider)
|
||||
// .getManager(walletId);
|
||||
final sendAmount = Decimal.parse(ref
|
||||
.read(estimatedRateExchangeFormProvider)
|
||||
.fromAmountString);
|
||||
|
|
|
@ -303,13 +303,17 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
|
||||
final String? path =
|
||||
await FilePicker.platform.getDirectoryPath(
|
||||
dialogTitle: "Choose Backup location",
|
||||
initialDirectory: dir.path,
|
||||
lockParentWindow: true,
|
||||
);
|
||||
String? path;
|
||||
if (Platform.isAndroid) {
|
||||
path = dir.path;
|
||||
} else {
|
||||
path = await FilePicker.platform
|
||||
.getDirectoryPath(
|
||||
dialogTitle: "Choose Backup location",
|
||||
initialDirectory: dir.path,
|
||||
lockParentWindow: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
final eventBus = EventBus();
|
||||
|
@ -328,7 +332,7 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
),
|
||||
));
|
||||
|
||||
await ref
|
||||
final filename = await ref
|
||||
.read(debugServiceProvider)
|
||||
.exportToFile(path, eventBus);
|
||||
|
||||
|
@ -336,10 +340,26 @@ class _DebugViewState extends ConsumerState<DebugView> {
|
|||
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
context: context,
|
||||
message: 'Logs file saved'));
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
unawaited(
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StackOkDialog(
|
||||
title: "Logs saved to",
|
||||
message: "${path!}/$filename",
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
context: context,
|
||||
message: 'Logs file saved',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -90,6 +90,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
break;
|
||||
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
try {
|
||||
final uri = Uri.parse(formData.host!);
|
||||
if (uri.scheme.startsWith("http")) {
|
||||
|
@ -384,6 +385,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
// strip unused path
|
||||
String address = formData.host!;
|
||||
if (coin == Coin.monero ||
|
||||
coin == Coin.wownero ||
|
||||
coin == Coin.epicCash) {
|
||||
if (address.startsWith("http")) {
|
||||
final uri = Uri.parse(address);
|
||||
|
@ -539,6 +541,7 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
|
||||
case Coin.epicCash:
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -699,7 +702,9 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
focusNode: _hostFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
(widget.coin != Coin.monero && widget.coin != Coin.epicCash)
|
||||
(widget.coin != Coin.monero &&
|
||||
widget.coin != Coin.wownero &&
|
||||
widget.coin != Coin.epicCash)
|
||||
? "IP address"
|
||||
: "Url",
|
||||
_hostFocusNode,
|
||||
|
@ -880,7 +885,9 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (widget.coin != Coin.monero && widget.coin != Coin.epicCash)
|
||||
if (widget.coin != Coin.monero &&
|
||||
widget.coin != Coin.wownero &&
|
||||
widget.coin != Coin.epicCash)
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
|
@ -931,11 +938,15 @@ class _NodeFormState extends ConsumerState<NodeForm> {
|
|||
),
|
||||
],
|
||||
),
|
||||
if (widget.coin != Coin.monero && widget.coin != Coin.epicCash)
|
||||
if (widget.coin != Coin.monero &&
|
||||
widget.coin != Coin.wownero &&
|
||||
widget.coin != Coin.epicCash)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (widget.coin != Coin.monero && widget.coin != Coin.epicCash)
|
||||
if (widget.coin != Coin.monero &&
|
||||
widget.coin != Coin.wownero &&
|
||||
widget.coin != Coin.epicCash)
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
|
|
|
@ -81,6 +81,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
break;
|
||||
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
try {
|
||||
final uri = Uri.parse(node!.host);
|
||||
if (uri.scheme.startsWith("http")) {
|
||||
|
|
|
@ -82,6 +82,17 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
passwordFocusNode = FocusNode();
|
||||
passwordRepeatFocusNode = FocusNode();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final dir = await stackFileSystem.prepareStorage();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text = dir.path;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -133,64 +144,70 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
TextField(
|
||||
onTap: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
if (!Platform.isAndroid)
|
||||
TextField(
|
||||
onTap: Platform.isAndroid
|
||||
? null
|
||||
: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
|
||||
if (mounted) {
|
||||
await stackFileSystem.pickDir(context);
|
||||
}
|
||||
if (mounted) {
|
||||
await stackFileSystem.pickDir(context);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.dirPath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Save to...",
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.dirPath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Save to...",
|
||||
hintStyle: STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
),
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
if (!Platform.isAndroid)
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -593,8 +610,15 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
|
|||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackOkDialog(
|
||||
title: "Stack Auto Backup enabled!"),
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup enabled and saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup enabled!"),
|
||||
);
|
||||
if (mounted) {
|
||||
passwordController.text = "";
|
||||
|
|
|
@ -64,6 +64,17 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
passwordFocusNode = FocusNode();
|
||||
passwordRepeatFocusNode = FocusNode();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final dir = await stackFileSystem.prepareStorage();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text = dir.path;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -113,88 +124,78 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Consumer(builder: (context, ref, __) {
|
||||
return Container(
|
||||
color: Colors.transparent,
|
||||
child: TextField(
|
||||
onTap: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
// ref
|
||||
// .read(
|
||||
// shouldShowLockscreenOnResumeStateProvider
|
||||
// .state)
|
||||
// .state = false;
|
||||
if (mounted) {
|
||||
await stackFileSystem.pickDir(context);
|
||||
}
|
||||
if (!Platform.isAndroid)
|
||||
Consumer(builder: (context, ref, __) {
|
||||
return Container(
|
||||
color: Colors.transparent,
|
||||
child: TextField(
|
||||
onTap: Platform.isAndroid
|
||||
? null
|
||||
: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
|
||||
// Future<void>.delayed(
|
||||
// const Duration(seconds: 2),
|
||||
// () => ref
|
||||
// .read(
|
||||
// shouldShowLockscreenOnResumeStateProvider
|
||||
// .state)
|
||||
// .state = true,
|
||||
// );
|
||||
if (mounted) {
|
||||
await stackFileSystem
|
||||
.pickDir(context);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.dirPath ?? "";
|
||||
});
|
||||
} catch (e, s) {
|
||||
// ref
|
||||
// .read(
|
||||
// shouldShowLockscreenOnResumeStateProvider
|
||||
// .state)
|
||||
// .state = true;
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Save to...",
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.dirPath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Save to...",
|
||||
hintStyle: STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
// ref.read(addressEntryDataProvider(widget.id)).address = newValue;
|
||||
},
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
// ref.read(addressEntryDataProvider(widget.id)).address = newValue;
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (!Platform.isAndroid)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -315,8 +316,12 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
.extension<StackColors>()!
|
||||
.accentColorRed
|
||||
: passwordStrength < 1
|
||||
? Theme.of(context).extension<StackColors>()!.accentColorYellow
|
||||
: Theme.of(context).extension<StackColors>()!.accentColorGreen,
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorYellow
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorGreen,
|
||||
backgroundColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
|
@ -389,8 +394,12 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
const Spacer(),
|
||||
TextButton(
|
||||
style: shouldEnableCreate
|
||||
? Theme.of(context) .extension<StackColors>()!.getPrimaryEnabledButtonColor(context)
|
||||
: Theme.of(context) .extension<StackColors>()!.getPrimaryDisabledButtonColor(context),
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonColor(context)
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryDisabledButtonColor(context),
|
||||
onPressed: !shouldEnableCreate
|
||||
? null
|
||||
: () async {
|
||||
|
@ -468,8 +477,14 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
|
|||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackOkDialog(
|
||||
title: "Backup creation succeeded"),
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title: "Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title:
|
||||
"Backup creation succeeded"),
|
||||
);
|
||||
passwordController.text = "";
|
||||
passwordRepeatController.text = "";
|
||||
|
|
|
@ -84,6 +84,17 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
passwordFocusNode = FocusNode();
|
||||
passwordRepeatFocusNode = FocusNode();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
final dir = await stackFileSystem.prepareStorage();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text = dir.path;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -135,64 +146,70 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
TextField(
|
||||
onTap: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
if (!Platform.isAndroid)
|
||||
TextField(
|
||||
onTap: Platform.isAndroid
|
||||
? null
|
||||
: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
|
||||
if (mounted) {
|
||||
await stackFileSystem.pickDir(context);
|
||||
}
|
||||
if (mounted) {
|
||||
await stackFileSystem.pickDir(context);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.dirPath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Save to...",
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.dirPath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Save to...",
|
||||
hintStyle: STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
),
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
key: const Key(
|
||||
"createBackupSaveToFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
if (!Platform.isAndroid)
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
onChanged: (newValue) {},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -594,8 +611,14 @@ class _EditAutoBackupViewState extends ConsumerState<EditAutoBackupView> {
|
|||
await showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => const StackOkDialog(
|
||||
title: "Stack Auto Backup saved"),
|
||||
builder: (_) => Platform.isAndroid
|
||||
? StackOkDialog(
|
||||
title:
|
||||
"Stack Auto Backup saved to:",
|
||||
message: fileToSave,
|
||||
)
|
||||
: const StackOkDialog(
|
||||
title: "Stack Auto Backup saved"),
|
||||
);
|
||||
if (mounted) {
|
||||
passwordController.text = "";
|
||||
|
|
|
@ -209,6 +209,10 @@ abstract class SWB {
|
|||
Logging.instance.log(
|
||||
"...createStackWalletJSON DB.instance.mutex acquired",
|
||||
level: LogLevel.Info);
|
||||
Logging.instance.log(
|
||||
"SWB backing up nodes",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
try {
|
||||
var primaryNodes = nodeService.primaryNodes.map((e) async {
|
||||
final map = e.toMap();
|
||||
|
@ -231,6 +235,11 @@ abstract class SWB {
|
|||
Logging.instance.log("$e $s", level: LogLevel.Error);
|
||||
}
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB backing up prefs",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
|
||||
Map<String, dynamic> prefs = {};
|
||||
final _prefs = Prefs.instance;
|
||||
await _prefs.init();
|
||||
|
@ -251,11 +260,21 @@ abstract class SWB {
|
|||
|
||||
backupJson['prefs'] = prefs;
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB backing up addressbook",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
|
||||
AddressBookService addressBookService = AddressBookService();
|
||||
var addresses = await addressBookService.addressBookEntries;
|
||||
backupJson['addressBookEntries'] =
|
||||
addresses.map((e) => e.toMap()).toList();
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB backing up wallets",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
|
||||
List<dynamic> backupWallets = [];
|
||||
for (var manager in _wallets.managers) {
|
||||
Map<String, dynamic> backupWallet = {};
|
||||
|
@ -283,6 +302,11 @@ abstract class SWB {
|
|||
}
|
||||
backupJson['wallets'] = backupWallets;
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB backing up trades",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
|
||||
// back up trade history
|
||||
final tradesService = TradesService();
|
||||
final trades =
|
||||
|
@ -295,6 +319,11 @@ abstract class SWB {
|
|||
tradeTxidLookupDataService.all.map((e) => e.toMap()).toList();
|
||||
backupJson["tradeTxidLookupData"] = lookupData;
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB backing up trade notes",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
|
||||
// back up trade notes
|
||||
final tradeNotesService = TradeNotesService();
|
||||
final tradeNotes = tradeNotesService.all;
|
||||
|
@ -357,7 +386,7 @@ abstract class SWB {
|
|||
final notes = walletbackup["notes"] as Map?;
|
||||
if (notes != null) {
|
||||
for (final note in notes.entries) {
|
||||
notesService.editOrAddNote(
|
||||
await notesService.editOrAddNote(
|
||||
txid: note.key as String, note: note.value as String);
|
||||
}
|
||||
}
|
||||
|
@ -432,11 +461,19 @@ abstract class SWB {
|
|||
|
||||
uiState?.preferences = StackRestoringStatus.restoring;
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB restoring prefs",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
await _restorePrefs(prefs);
|
||||
|
||||
uiState?.preferences = StackRestoringStatus.success;
|
||||
uiState?.addressBook = StackRestoringStatus.restoring;
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB restoring addressbook",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
if (addressBookEntries != null) {
|
||||
await _restoreAddressBook(addressBookEntries);
|
||||
}
|
||||
|
@ -444,6 +481,10 @@ abstract class SWB {
|
|||
uiState?.addressBook = StackRestoringStatus.success;
|
||||
uiState?.nodes = StackRestoringStatus.restoring;
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB restoring nodes",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
await _restoreNodes(nodes, primaryNodes);
|
||||
|
||||
uiState?.nodes = StackRestoringStatus.success;
|
||||
|
@ -451,17 +492,29 @@ abstract class SWB {
|
|||
|
||||
// restore trade history
|
||||
if (trades != null) {
|
||||
Logging.instance.log(
|
||||
"SWB restoring trades",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
await _restoreTrades(trades);
|
||||
}
|
||||
|
||||
// restore trade history lookup data for trades send from stack wallet
|
||||
if (tradeTxidLookupData != null) {
|
||||
Logging.instance.log(
|
||||
"SWB restoring trade look up data",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
await _restoreTradesLookUpData(tradeTxidLookupData, oldToNewWalletIdMap);
|
||||
}
|
||||
|
||||
// restore trade notes
|
||||
|
||||
if (tradeNotes != null) {
|
||||
Logging.instance.log(
|
||||
"SWB restoring trade notes",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
await _restoreTradesNotes(tradeNotes);
|
||||
}
|
||||
|
||||
|
@ -490,9 +543,17 @@ abstract class SWB {
|
|||
String jsonBackup,
|
||||
StackRestoringUIState? uiState,
|
||||
) async {
|
||||
if (!Platform.isLinux) Wakelock.enable();
|
||||
if (!Platform.isLinux) await Wakelock.enable();
|
||||
|
||||
Logging.instance.log(
|
||||
"SWB creating temp backup",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
final preRestoreJSON = await createStackWalletJSON();
|
||||
Logging.instance.log(
|
||||
"SWB temp backup created",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
|
||||
List<String> _currentWalletIds = Map<String, dynamic>.from(DB.instance
|
||||
.get<dynamic>(
|
||||
|
@ -814,13 +875,13 @@ abstract class SWB {
|
|||
}
|
||||
await asyncRestore(epicCashWallets[i], uiState, walletsService);
|
||||
}
|
||||
if (!Platform.isLinux) Wakelock.disable();
|
||||
if (!Platform.isLinux) await Wakelock.disable();
|
||||
// check if cancel was requested and restore previous state
|
||||
if (_checkShouldCancel(preRestoreState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.instance.log("done with SWB restore", level: LogLevel.Info);
|
||||
Logging.instance.log("done with SWB restore", level: LogLevel.Warning);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -849,7 +910,7 @@ abstract class SWB {
|
|||
// if no contacts were present before attempted restore then delete any that
|
||||
// could have been added before the restore was cancelled
|
||||
for (final String idToDelete in allContactIds) {
|
||||
addressBookService.removeContact(idToDelete);
|
||||
await addressBookService.removeContact(idToDelete);
|
||||
}
|
||||
} else {
|
||||
final Map<String, dynamic> preContactMap = {};
|
||||
|
@ -886,7 +947,7 @@ abstract class SWB {
|
|||
);
|
||||
} else {
|
||||
// otherwise remove it as it was not there before attempting SWB restore
|
||||
addressBookService.removeContact(id);
|
||||
await addressBookService.removeContact(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -898,7 +959,7 @@ abstract class SWB {
|
|||
// no pre nodes found so we delete all but defaults
|
||||
for (final node in currentNodes) {
|
||||
if (!node.isDefault) {
|
||||
nodeService.delete(node.id, true);
|
||||
await nodeService.delete(node.id, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -912,7 +973,7 @@ abstract class SWB {
|
|||
if (nodeData != null) {
|
||||
// node existed before restore attempt
|
||||
// revert to pre restore node
|
||||
nodeService.edit(
|
||||
await nodeService.edit(
|
||||
node.copyWith(
|
||||
host: nodeData['host'] as String,
|
||||
port: nodeData['port'] as int,
|
||||
|
@ -927,7 +988,7 @@ abstract class SWB {
|
|||
nodeData['password'] as String?,
|
||||
true);
|
||||
} else {
|
||||
nodeService.delete(node.id, true);
|
||||
await nodeService.delete(node.id, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -951,7 +1012,7 @@ abstract class SWB {
|
|||
// no trade history found pre restore attempt so we delete anything that
|
||||
// was added during the restore attempt
|
||||
for (final tradeTx in currentTrades) {
|
||||
tradesService.delete(trade: tradeTx, shouldNotifyListeners: true);
|
||||
await tradesService.delete(trade: tradeTx, shouldNotifyListeners: true);
|
||||
}
|
||||
} else {
|
||||
final Map<String, dynamic> preTradeMap = {};
|
||||
|
@ -964,13 +1025,14 @@ abstract class SWB {
|
|||
if (tradeData != null) {
|
||||
// trade existed before attempted restore so we don't delete it, only
|
||||
// revert data to pre restore state
|
||||
tradesService.edit(
|
||||
await tradesService.edit(
|
||||
trade: ExchangeTransaction.fromJson(
|
||||
tradeData as Map<String, dynamic>),
|
||||
shouldNotifyListeners: true);
|
||||
} else {
|
||||
// trade did not exist before so we delete it
|
||||
tradesService.delete(trade: tradeTx, shouldNotifyListeners: true);
|
||||
await tradesService.delete(
|
||||
trade: tradeTx, shouldNotifyListeners: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -982,7 +1044,7 @@ abstract class SWB {
|
|||
|
||||
if (tradeNotes == null) {
|
||||
for (final noteEntry in currentNotes.entries) {
|
||||
tradeNotesService.delete(tradeId: noteEntry.key);
|
||||
await tradeNotesService.delete(tradeId: noteEntry.key);
|
||||
}
|
||||
} else {
|
||||
// grab all trade IDs of (reverted to pre state) trades
|
||||
|
@ -991,7 +1053,7 @@ abstract class SWB {
|
|||
// delete all notes that don't correspond to an id that we have
|
||||
for (final noteEntry in currentNotes.entries) {
|
||||
if (!idsToKeep.contains(noteEntry.key)) {
|
||||
tradeNotesService.delete(tradeId: noteEntry.key);
|
||||
await tradeNotesService.delete(tradeId: noteEntry.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1009,7 +1071,7 @@ abstract class SWB {
|
|||
for (int i = 0; i < tradeTxidLookupData.length; i++) {
|
||||
final json = Map<String, dynamic>.from(tradeTxidLookupData[i] as Map);
|
||||
TradeWalletLookup lookup = TradeWalletLookup.fromJson(json);
|
||||
tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
|
||||
await tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1127,14 +1189,14 @@ abstract class SWB {
|
|||
) async {
|
||||
final tradesService = TradesService();
|
||||
for (int i = 0; i < trades.length - 1; i++) {
|
||||
tradesService.add(
|
||||
await tradesService.add(
|
||||
trade: ExchangeTransaction.fromJson(trades[i] as Map<String, dynamic>),
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
}
|
||||
// only call notifyListeners on last one added
|
||||
if (trades.isNotEmpty) {
|
||||
tradesService.add(
|
||||
await tradesService.add(
|
||||
trade:
|
||||
ExchangeTransaction.fromJson(trades.last as Map<String, dynamic>),
|
||||
shouldNotifyListeners: true,
|
||||
|
@ -1177,7 +1239,7 @@ abstract class SWB {
|
|||
}
|
||||
}
|
||||
|
||||
tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
|
||||
await tradeTxidLookupDataService.save(tradeWalletLookup: lookup);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1248,8 @@ abstract class SWB {
|
|||
) async {
|
||||
final tradeNotesService = TradeNotesService();
|
||||
for (final note in tradeNotes.entries) {
|
||||
tradeNotesService.set(tradeId: note.key, note: note.value as String);
|
||||
await tradeNotesService.set(
|
||||
tradeId: note.key, note: note.value as String);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ class StackFileSystem {
|
|||
|
||||
final bool isDesktop = !(Platform.isAndroid || Platform.isIOS);
|
||||
|
||||
Future<void> prepareStorage() async {
|
||||
Future<Directory> prepareStorage() async {
|
||||
rootPath = (await getApplicationDocumentsDirectory());
|
||||
debugPrint(rootPath!.absolute.toString());
|
||||
if (Platform.isAndroid) {
|
||||
|
@ -47,6 +47,7 @@ class StackFileSystem {
|
|||
debugPrint("$e $s");
|
||||
}
|
||||
startPath = sampleFolder;
|
||||
return sampleFolder;
|
||||
}
|
||||
|
||||
Future<void> pickDir(BuildContext context) async {
|
||||
|
|
|
@ -99,29 +99,17 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
onTap: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
// ref
|
||||
// .read(shouldShowLockscreenOnResumeStateProvider
|
||||
// .state)
|
||||
// .state = false;
|
||||
await stackFileSystem.openFile(context);
|
||||
if (mounted) {
|
||||
await stackFileSystem.openFile(context);
|
||||
}
|
||||
|
||||
// Future<void>.delayed(
|
||||
// const Duration(seconds: 2),
|
||||
// () => ref
|
||||
// .read(
|
||||
// shouldShowLockscreenOnResumeStateProvider
|
||||
// .state)
|
||||
// .state = true,
|
||||
// );
|
||||
|
||||
fileLocationController.text =
|
||||
stackFileSystem.filePath ?? "";
|
||||
setState(() {});
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.filePath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
// ref
|
||||
// .read(shouldShowLockscreenOnResumeStateProvider
|
||||
// .state)
|
||||
// .state = true;
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
|
@ -130,6 +118,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
|
|||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Choose file...",
|
||||
hintStyle: STextStyles.fieldLabel(context),
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_net
|
|||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/blocks_remaining_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/refresh_percent_changed_event.dart';
|
||||
|
@ -205,7 +206,7 @@ class _WalletNetworkSettingsViewState
|
|||
.getManager(widget.walletId)
|
||||
.coin;
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.epicCash) {
|
||||
if (coin == Coin.monero || coin == Coin.wownero || coin == Coin.epicCash) {
|
||||
_blocksRemainingSubscription = eventBus.on<BlocksRemainingEvent>().listen(
|
||||
(event) async {
|
||||
if (event.walletId == widget.walletId) {
|
||||
|
@ -271,6 +272,15 @@ class _WalletNetworkSettingsViewState
|
|||
if (_percent < highestPercent) {
|
||||
_percent = highestPercent.clamp(0.0, 1.0);
|
||||
}
|
||||
} else if (coin == Coin.wownero) {
|
||||
double highestPercent = (ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(widget.walletId)
|
||||
.wallet as WowneroWallet)
|
||||
.highestPercentCached;
|
||||
if (_percent < highestPercent) {
|
||||
_percent = highestPercent.clamp(0.0, 1.0);
|
||||
}
|
||||
} else if (coin == Coin.epicCash) {
|
||||
double highestPercent = (ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
|
@ -545,6 +555,7 @@ class _WalletNetworkSettingsViewState
|
|||
),
|
||||
),
|
||||
if (coin == Coin.monero ||
|
||||
coin == Coin.wownero ||
|
||||
coin == Coin.epicCash)
|
||||
Text(
|
||||
" (Blocks to go: ${_blocksRemaining == -1 ? "?" : _blocksRemaining})",
|
||||
|
|
|
@ -9,7 +9,12 @@ import 'package:stackwallet/services/coins/manager.dart';
|
|||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/trade_card.dart';
|
||||
import 'package:stackwallet/widgets/transaction_card.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../providers/global/trades_service_provider.dart';
|
||||
import '../../exchange_view/trade_details_view.dart';
|
||||
|
||||
class TransactionsList extends ConsumerStatefulWidget {
|
||||
const TransactionsList({
|
||||
|
@ -125,18 +130,67 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
|
|||
radius = _borderRadiusFirst;
|
||||
}
|
||||
final tx = list[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: TransactionCard(
|
||||
// this may mess with combined firo transactions
|
||||
key: Key(tx.toString()), //
|
||||
transaction: tx,
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
);
|
||||
|
||||
final matchingTrades = ref
|
||||
.read(tradesServiceProvider)
|
||||
.trades
|
||||
.where((e) =>
|
||||
e.statusObject != null &&
|
||||
(e.statusObject!.payinHash == tx.txid ||
|
||||
e.statusObject!.payoutHash == tx.txid));
|
||||
if (tx.txType == "Sent" && matchingTrades.isNotEmpty) {
|
||||
final trade = matchingTrades.first;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TransactionCard(
|
||||
// this may mess with combined firo transactions
|
||||
key: Key(tx.toString()), //
|
||||
transaction: tx,
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
TradeCard(
|
||||
// this may mess with combined firo transactions
|
||||
key: Key(tx.toString() + trade.uuid), //
|
||||
trade: trade,
|
||||
onTap: () {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
TradeDetailsView.routeName,
|
||||
arguments: Tuple4(
|
||||
trade.id,
|
||||
tx,
|
||||
widget.walletId,
|
||||
ref.read(managerProvider).walletName,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: radius,
|
||||
),
|
||||
child: TransactionCard(
|
||||
// this may mess with combined firo transactions
|
||||
key: Key(tx.toString()), //
|
||||
transaction: tx,
|
||||
walletId: widget.walletId,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
@ -241,7 +241,9 @@ class _TransactionDetailsViewState
|
|||
"$amountPrefix${Format.localizedStringAsFixed(
|
||||
value: coin == Coin.monero
|
||||
? (amount / 10000.toDecimal()).toDecimal()
|
||||
: amount,
|
||||
: coin == Coin.wownero
|
||||
? (amount / 1000.toDecimal()).toDecimal()
|
||||
: amount,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
|
@ -254,7 +256,7 @@ class _TransactionDetailsViewState
|
|||
height: 2,
|
||||
),
|
||||
SelectableText(
|
||||
"${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch(
|
||||
"${Format.localizedStringAsFixed(value: (coin == Coin.monero ? (amount / 10000.toDecimal()).toDecimal() : coin == Coin.wownero ? (amount / 1000.toDecimal()).toDecimal() : amount) * ref.watch(priceAnd24hChangeNotifierProvider.select((value) => value.getPrice(coin).item1)), locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale),
|
||||
), decimalPlaces: 2)} ${ref.watch(
|
||||
|
@ -298,14 +300,14 @@ class _TransactionDetailsViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
if (!(coin == Coin.monero &&
|
||||
if (!((coin == Coin.monero || coin == Coin.wownero) &&
|
||||
_transaction.txType.toLowerCase() == "sent") &&
|
||||
!((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
_transaction.subType == "mint"))
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (!(coin == Coin.monero &&
|
||||
if (!((coin == Coin.monero || coin == Coin.wownero) &&
|
||||
_transaction.txType.toLowerCase() == "sent") &&
|
||||
!((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
_transaction.subType == "mint"))
|
||||
|
@ -464,7 +466,10 @@ class _TransactionDetailsViewState
|
|||
? Format.localizedStringAsFixed(
|
||||
value: coin == Coin.monero
|
||||
? (fee / 10000.toDecimal()).toDecimal()
|
||||
: fee,
|
||||
: coin == Coin.wownero
|
||||
? (fee / 1000.toDecimal())
|
||||
.toDecimal()
|
||||
: fee,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale)),
|
||||
|
@ -473,7 +478,9 @@ class _TransactionDetailsViewState
|
|||
: Format.localizedStringAsFixed(
|
||||
value: coin == Coin.monero
|
||||
? (fee / 10000.toDecimal()).toDecimal()
|
||||
: fee,
|
||||
: coin == Coin.wownero
|
||||
? (fee / 1000.toDecimal()).toDecimal()
|
||||
: fee,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select((value) => value.locale)),
|
||||
|
|
|
@ -755,6 +755,11 @@ class _TransactionSearchViewState
|
|||
.floor()
|
||||
.toBigInt()
|
||||
.toInt();
|
||||
} else if (widget.coin == Coin.wownero) {
|
||||
amount = (amountDecimal * Decimal.fromInt(Constants.satsPerCoinWownero))
|
||||
.floor()
|
||||
.toBigInt()
|
||||
.toInt();
|
||||
} else {
|
||||
amount = (amountDecimal * Decimal.fromInt(Constants.satsPerCoin))
|
||||
.floor()
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart';
|
||||
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
|
||||
import 'package:stackwallet/models/paymint/transactions_model.dart';
|
||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||
|
@ -20,12 +22,14 @@ import 'package:stackwallet/pages/address_book_views/subviews/address_book_filte
|
|||
import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart';
|
||||
import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_1_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_4_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/send_from_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
|
||||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||
|
@ -879,6 +883,37 @@ class RouteGenerator {
|
|||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case ChooseFromStackView.routeName:
|
||||
if (args is Coin) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => ChooseFromStackView(
|
||||
coin: args,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case SendFromView.routeName:
|
||||
if (args is Tuple4<Coin, Decimal, String, ExchangeTransaction>) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => SendFromView(
|
||||
coin: args.item1,
|
||||
amount: args.item2,
|
||||
trade: args.item4,
|
||||
address: args.item3,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
// == Desktop specific routes ============================================
|
||||
case CreatePasswordView.routeName:
|
||||
return getRoute(
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'dart:typed_data';
|
|||
import 'package:bech32/bech32.dart';
|
||||
import 'package:bip32/bip32.dart' as bip32;
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:bitbox/bitbox.dart' as Bitbox;
|
||||
import 'package:bitcoindart/bitcoindart.dart';
|
||||
import 'package:bs58check/bs58check.dart' as bs58check;
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
@ -41,7 +42,6 @@ import 'package:stackwallet/utilities/logger.dart';
|
|||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:bitbox/bitbox.dart' as Bitbox;
|
||||
|
||||
const int MINIMUM_CONFIRMATIONS = 3;
|
||||
const int DUST_LIMIT = 546;
|
||||
|
|
|
@ -4,11 +4,12 @@ import 'package:stackwallet/electrumx_rpc/electrumx.dart';
|
|||
import 'package:stackwallet/models/models.dart';
|
||||
import 'package:stackwallet/models/node_model.dart';
|
||||
import 'package:stackwallet/services/coins/bitcoin/bitcoin_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/bitcoincash/bitcoincash_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
|
||||
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
|
||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
@ -145,6 +146,14 @@ abstract class CoinServiceAPI {
|
|||
// tracker: tracker,
|
||||
);
|
||||
|
||||
case Coin.wownero:
|
||||
return WowneroWallet(
|
||||
walletId: walletId,
|
||||
walletName: walletName,
|
||||
coin: coin,
|
||||
// tracker: tracker,
|
||||
);
|
||||
|
||||
case Coin.namecoin:
|
||||
return NamecoinWallet(
|
||||
walletId: walletId,
|
||||
|
|
|
@ -259,6 +259,9 @@ Future<String> deleteEpicWallet({
|
|||
if (Platform.isIOS) {
|
||||
appDir = (await getLibraryDirectory());
|
||||
}
|
||||
if (Platform.isLinux) {
|
||||
appDir = Directory("${appDir.path}/.stackwallet");
|
||||
}
|
||||
final path = "${appDir.path}/epiccash";
|
||||
final String name = walletId;
|
||||
|
||||
|
@ -1232,6 +1235,9 @@ class EpicCashWallet extends CoinServiceAPI {
|
|||
if (Platform.isIOS) {
|
||||
appDir = (await getLibraryDirectory());
|
||||
}
|
||||
if (Platform.isLinux) {
|
||||
appDir = Directory("${appDir.path}/.stackwallet");
|
||||
}
|
||||
final path = "${appDir.path}/epiccash";
|
||||
final String name = _walletId.trim();
|
||||
return '$path/$name';
|
||||
|
|
|
@ -910,6 +910,10 @@ class MoneroWallet extends CoinServiceAPI {
|
|||
if (Platform.isIOS) {
|
||||
root = (await getLibraryDirectory());
|
||||
}
|
||||
//
|
||||
if (Platform.isLinux) {
|
||||
root = Directory("${root.path}/.stackwallet");
|
||||
}
|
||||
final prefix = walletTypeToString(type).toLowerCase();
|
||||
final walletsDir = Directory('${root.path}/wallets');
|
||||
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
|
||||
|
|
1563
lib/services/coins/wownero/wownero_wallet.dart
Normal file
1563
lib/services/coins/wownero/wownero_wallet.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -75,7 +75,8 @@ class DebugService extends ChangeNotifier {
|
|||
level: LogLevel.Info);
|
||||
}
|
||||
|
||||
Future<void> exportToFile(String directory, EventBus eventBus) async {
|
||||
/// returns the filename of the saved logs file
|
||||
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";
|
||||
|
@ -99,5 +100,6 @@ class DebugService extends ChangeNotifier {
|
|||
await sink.close();
|
||||
|
||||
eventBus.fire(1.0);
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ class PriceAPI {
|
|||
Map<Coin, Tuple2<Decimal, double>> result = {};
|
||||
try {
|
||||
final uri = Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false");
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false");
|
||||
// final uri = Uri.parse(
|
||||
// "https://api.coingecko.com/api/v3/coins/markets?vs_currency=${baseCurrency.toLowerCase()}&ids=monero%2Cbitcoin%2Cepic-cash%2Czcoin%2Cdogecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false");
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ class Wallets extends ChangeNotifier {
|
|||
final shouldSetAutoSync = shouldAutoSyncAll ||
|
||||
walletIdsToEnableAutoSync.contains(manager.walletId);
|
||||
|
||||
if (manager.coin == Coin.monero) {
|
||||
if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
|
||||
walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
||||
} else {
|
||||
walletInitFutures.add(manager.initializeExisting().then((value) {
|
||||
|
@ -312,7 +312,7 @@ class Wallets extends ChangeNotifier {
|
|||
final shouldSetAutoSync = shouldAutoSyncAll ||
|
||||
walletIdsToEnableAutoSync.contains(manager.walletId);
|
||||
|
||||
if (manager.coin == Coin.monero) {
|
||||
if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
|
||||
walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
|
||||
} else {
|
||||
walletInitFutures.add(manager.initializeExisting().then((value) {
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_libmonero/monero/monero.dart';
|
||||
import 'package:flutter_libmonero/wownero/wownero.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:stackwallet/hive/db.dart';
|
||||
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
|
||||
|
@ -367,8 +368,13 @@ class WalletsService extends ChangeNotifier {
|
|||
await DB.instance.delete<dynamic>(
|
||||
boxName: DB.boxNameAllWalletsData,
|
||||
key: "${walletId}_mnemonicHasBeenVerified");
|
||||
|
||||
if (coinFromPrettyName(shell['coin'] as String) == Coin.monero) {
|
||||
if (coinFromPrettyName(shell['coin'] as String) == Coin.wownero) {
|
||||
final wowService =
|
||||
wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
|
||||
await wowService.remove(walletId);
|
||||
Logging.instance
|
||||
.log("monero wallet: $walletId deleted", level: LogLevel.Info);
|
||||
} else if (coinFromPrettyName(shell['coin'] as String) == Coin.monero) {
|
||||
final xmrService =
|
||||
monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
|
||||
await xmrService.remove(walletId);
|
||||
|
|
|
@ -53,6 +53,9 @@ class AddressUtils {
|
|||
case Coin.monero:
|
||||
return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) ||
|
||||
RegExp("[a-zA-Z0-9]{106}").hasMatch(address);
|
||||
case Coin.wownero:
|
||||
return RegExp("[a-zA-Z0-9]{95}").hasMatch(address) ||
|
||||
RegExp("[a-zA-Z0-9]{106}").hasMatch(address);
|
||||
case Coin.namecoin:
|
||||
return Address.validateAddress(address, namecoin, namecoin.bech32!);
|
||||
case Coin.bitcoinTestNet:
|
||||
|
|
|
@ -130,6 +130,7 @@ class _SVG {
|
|||
String get epicCash => "assets/svg/coin_icons/EpicCash.svg";
|
||||
String get firo => "assets/svg/coin_icons/Firo.svg";
|
||||
String get monero => "assets/svg/coin_icons/Monero.svg";
|
||||
String get wownero => "assets/svg/coin_icons/Wownero.svg";
|
||||
String get namecoin => "assets/svg/coin_icons/Namecoin.svg";
|
||||
|
||||
String get chevronRight => "assets/svg/chevron-right.svg";
|
||||
|
@ -158,6 +159,8 @@ class _SVG {
|
|||
return firo;
|
||||
case Coin.monero:
|
||||
return monero;
|
||||
case Coin.wownero:
|
||||
return wownero;
|
||||
case Coin.namecoin:
|
||||
return namecoin;
|
||||
case Coin.bitcoinTestNet:
|
||||
|
@ -179,6 +182,7 @@ class _PNG {
|
|||
String get splash => "assets/images/splash.png";
|
||||
|
||||
String get monero => "assets/images/monero.png";
|
||||
String get wownero => "assets/images/wownero.png";
|
||||
String get firo => "assets/images/firo.png";
|
||||
String get dogecoin => "assets/images/doge.png";
|
||||
String get bitcoin => "assets/images/bitcoin.png";
|
||||
|
@ -205,6 +209,8 @@ class _PNG {
|
|||
return firo;
|
||||
case Coin.monero:
|
||||
return monero;
|
||||
case Coin.wownero:
|
||||
return wownero;
|
||||
case Coin.namecoin:
|
||||
return namecoin;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ Uri getBlockExplorerTransactionUrlFor({
|
|||
throw UnimplementedError("missing block explorer for epic cash");
|
||||
case Coin.monero:
|
||||
return Uri.parse("https://xmrchain.net/tx/$txid");
|
||||
case Coin.wownero:
|
||||
return Uri.parse("https://explore.wownero.com/search?value=$txid");
|
||||
case Coin.firo:
|
||||
return Uri.parse("https://explorer.firo.org/tx/$txid");
|
||||
case Coin.firoTestNet:
|
||||
|
|
|
@ -18,8 +18,11 @@ abstract class Constants {
|
|||
|
||||
//TODO: correct for monero?
|
||||
static const int satsPerCoinMonero = 1000000000000;
|
||||
static const int satsPerCoinWownero = 100000000000;
|
||||
static const int satsPerCoin = 100000000;
|
||||
static const int decimalPlaces = 8;
|
||||
static const int decimalPlacesWownero = 11;
|
||||
static const int decimalPlacesMonero = 12;
|
||||
|
||||
static const int notificationsMax = 0xFFFFFFFF;
|
||||
static const Duration networkAliveTimerDuration = Duration(seconds: 10);
|
||||
|
@ -40,6 +43,7 @@ abstract class Constants {
|
|||
switch (coin) {
|
||||
case Coin.bitcoin:
|
||||
case Coin.bitcoincash:
|
||||
case Coin.bitcoincashTestnet:
|
||||
case Coin.dogecoin:
|
||||
case Coin.firo:
|
||||
case Coin.bitcoinTestNet:
|
||||
|
@ -53,6 +57,9 @@ abstract class Constants {
|
|||
case Coin.monero:
|
||||
values.addAll([25]);
|
||||
break;
|
||||
case Coin.wownero:
|
||||
values.addAll([14]);
|
||||
break;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
@ -82,6 +89,9 @@ abstract class Constants {
|
|||
case Coin.monero:
|
||||
return 120;
|
||||
|
||||
case Coin.wownero:
|
||||
return 120;
|
||||
|
||||
case Coin.namecoin:
|
||||
return 600;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ abstract class DefaultNodes {
|
|||
epicCash,
|
||||
bitcoincash,
|
||||
namecoin,
|
||||
wownero,
|
||||
bitcoinTestnet,
|
||||
bitcoincashTestnet,
|
||||
dogecoinTestnet,
|
||||
firoTestnet,
|
||||
];
|
||||
|
@ -82,6 +84,20 @@ abstract class DefaultNodes {
|
|||
isDown: false,
|
||||
);
|
||||
|
||||
// TODO: eventually enable ssl and set scheme to https
|
||||
// currently get certificate failure
|
||||
static NodeModel get wownero => NodeModel(
|
||||
host: "http://eu-west-2.wow.xmr.pm",
|
||||
port: 34568,
|
||||
name: defaultName,
|
||||
id: _nodeId(Coin.wownero),
|
||||
useSSL: false,
|
||||
enabled: true,
|
||||
coinName: Coin.wownero.name,
|
||||
isFailover: true,
|
||||
isDown: false,
|
||||
);
|
||||
|
||||
static NodeModel get epicCash => NodeModel(
|
||||
host: "http://epiccash.stackwallet.com",
|
||||
port: 3413,
|
||||
|
@ -174,6 +190,9 @@ abstract class DefaultNodes {
|
|||
case Coin.monero:
|
||||
return monero;
|
||||
|
||||
case Coin.wownero:
|
||||
return wownero;
|
||||
|
||||
case Coin.namecoin:
|
||||
return namecoin;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as firo;
|
|||
import 'package:stackwallet/services/coins/monero/monero_wallet.dart' as xmr;
|
||||
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'
|
||||
as nmc;
|
||||
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart' as wow;
|
||||
|
||||
enum Coin {
|
||||
bitcoin,
|
||||
|
@ -17,6 +18,7 @@ enum Coin {
|
|||
epicCash,
|
||||
firo,
|
||||
monero,
|
||||
wownero,
|
||||
namecoin,
|
||||
|
||||
///
|
||||
|
@ -47,6 +49,8 @@ extension CoinExt on Coin {
|
|||
return "Firo";
|
||||
case Coin.monero:
|
||||
return "Monero";
|
||||
case Coin.wownero:
|
||||
return "Wownero";
|
||||
case Coin.namecoin:
|
||||
return "Namecoin";
|
||||
case Coin.bitcoinTestNet:
|
||||
|
@ -74,6 +78,8 @@ extension CoinExt on Coin {
|
|||
return "FIRO";
|
||||
case Coin.monero:
|
||||
return "XMR";
|
||||
case Coin.wownero:
|
||||
return "WOW";
|
||||
case Coin.namecoin:
|
||||
return "NMC";
|
||||
case Coin.bitcoinTestNet:
|
||||
|
@ -102,6 +108,8 @@ extension CoinExt on Coin {
|
|||
return "firo";
|
||||
case Coin.monero:
|
||||
return "monero";
|
||||
case Coin.wownero:
|
||||
return "wownero";
|
||||
case Coin.namecoin:
|
||||
return "namecoin";
|
||||
case Coin.bitcoinTestNet:
|
||||
|
@ -130,6 +138,7 @@ extension CoinExt on Coin {
|
|||
|
||||
case Coin.epicCash:
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -157,6 +166,10 @@ extension CoinExt on Coin {
|
|||
|
||||
case Coin.monero:
|
||||
return xmr.MINIMUM_CONFIRMATIONS;
|
||||
|
||||
case Coin.wownero:
|
||||
return wow.MINIMUM_CONFIRMATIONS;
|
||||
|
||||
case Coin.namecoin:
|
||||
return nmc.MINIMUM_CONFIRMATIONS;
|
||||
}
|
||||
|
@ -204,6 +217,10 @@ Coin coinFromPrettyName(String name) {
|
|||
case "tDogecoin":
|
||||
case "dogecoinTestNet":
|
||||
return Coin.dogecoinTestNet;
|
||||
case "Wownero":
|
||||
case "tWownero":
|
||||
case "wownero":
|
||||
return Coin.wownero;
|
||||
default:
|
||||
throw ArgumentError.value(
|
||||
name, "name", "No Coin enum value with that prettyName");
|
||||
|
@ -234,6 +251,8 @@ Coin coinFromTickerCaseInsensitive(String ticker) {
|
|||
return Coin.firoTestNet;
|
||||
case "tdoge":
|
||||
return Coin.dogecoinTestNet;
|
||||
case "wow":
|
||||
return Coin.wownero;
|
||||
default:
|
||||
throw ArgumentError.value(
|
||||
ticker, "name", "No Coin enum value with that ticker");
|
||||
|
|
|
@ -209,8 +209,8 @@ class CoinThemeColor {
|
|||
return monero;
|
||||
case Coin.namecoin:
|
||||
return namecoin;
|
||||
// case Coin.wownero:
|
||||
// return wownero;
|
||||
case Coin.wownero:
|
||||
return wownero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1422,8 +1422,8 @@ class StackColors extends ThemeExtension<StackColors> {
|
|||
return _coin.monero;
|
||||
case Coin.namecoin:
|
||||
return _coin.namecoin;
|
||||
// case Coin.wownero:
|
||||
// return wownero;
|
||||
case Coin.wownero:
|
||||
return _coin.wownero;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
break;
|
||||
|
||||
case Coin.monero:
|
||||
case Coin.wownero:
|
||||
try {
|
||||
final uri = Uri.parse(node.host);
|
||||
if (uri.scheme.startsWith("http")) {
|
||||
|
|
|
@ -172,7 +172,9 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
|
|||
builder: (_) {
|
||||
final amount = coin == Coin.monero
|
||||
? (_transaction.amount ~/ 10000)
|
||||
: _transaction.amount;
|
||||
: coin == Coin.wownero
|
||||
? (_transaction.amount ~/ 1000)
|
||||
: _transaction.amount;
|
||||
return Text(
|
||||
"${Format.satoshiAmountToPrettyString(amount, locale)} ${coin.ticker}",
|
||||
style:
|
||||
|
@ -212,6 +214,8 @@ class _TransactionCardState extends ConsumerState<TransactionCard> {
|
|||
int value = _transaction.amount;
|
||||
if (coin == Coin.monero) {
|
||||
value = (value ~/ 10000);
|
||||
} else if (coin == Coin.wownero) {
|
||||
value = (value ~/ 1000);
|
||||
}
|
||||
|
||||
return Text(
|
||||
|
|
|
@ -137,6 +137,9 @@ install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
|||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_monero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libmonero/scripts/linux/build/libcw_wownero.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto_plugins/flutter_libepiccash/scripts/linux/build/rust/target/x86_64-unknown-linux-gnu/release/libepic_cash_wallet.so" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
|
|
|
@ -317,6 +317,13 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
cw_wownero:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "crypto_plugins/flutter_libmonero/cw_wownero"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
dart_numerics:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -11,7 +11,7 @@ description: Stack Wallet
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 1.4.45+60
|
||||
version: 1.4.53+69
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
@ -31,6 +31,9 @@ dependencies:
|
|||
cw_monero:
|
||||
path: ./crypto_plugins/flutter_libmonero/cw_monero
|
||||
|
||||
cw_wownero:
|
||||
path: ./crypto_plugins/flutter_libmonero/cw_wownero
|
||||
|
||||
cw_core:
|
||||
path: ./crypto_plugins/flutter_libmonero/cw_core
|
||||
|
||||
|
@ -189,6 +192,7 @@ flutter:
|
|||
- assets/svg/clipboard.svg
|
||||
- assets/images/stack.png
|
||||
- assets/images/monero.png
|
||||
- assets/images/wownero.png
|
||||
- assets/images/firo.png
|
||||
- assets/images/doge.png
|
||||
- assets/images/bitcoin.png
|
||||
|
@ -293,6 +297,7 @@ flutter:
|
|||
- assets/svg/coin_icons/EpicCash.svg
|
||||
- assets/svg/coin_icons/Firo.svg
|
||||
- assets/svg/coin_icons/Monero.svg
|
||||
- assets/svg/coin_icons/Wownero.svg
|
||||
- assets/svg/coin_icons/Namecoin.svg
|
||||
# lottie animations
|
||||
- assets/lottie/test.json
|
||||
|
|
0
scripts/ios/build_all.sh
Normal file → Executable file
0
scripts/ios/build_all.sh
Normal file → Executable file
6
scripts/prebuild.sh
Normal file
6
scripts/prebuild.sh
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Create template lib/external_api_keys.dart file if it doesn't already exist
|
||||
KEYS=../lib/external_api_keys.dart
|
||||
if ! test -f "$KEYS"; then
|
||||
echo 'prebuild.sh: creating template lib/external_api_keys.dart file'
|
||||
echo 'const kChangeNowApiKey = "";' > $KEYS
|
||||
fi
|
|
@ -1,7 +1,152 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:stackwallet/models/models.dart';
|
||||
|
||||
import '../services/coins/firo/sample_data/transaction_data_samples.dart';
|
||||
|
||||
void main() {
|
||||
group("TransactionData", () {
|
||||
test("TransactionData from Json", () {
|
||||
final txChunk = TransactionChunk.fromJson({
|
||||
"timestamp": 993260735,
|
||||
"transactions": [
|
||||
{
|
||||
"txid": "txid",
|
||||
"confirmed_status": true,
|
||||
"timestamp": 1876352482,
|
||||
"txType": "txType",
|
||||
"amount": 10,
|
||||
"worthNow": "1",
|
||||
"worthAtBlockTimestamp": "1",
|
||||
"fees": 1,
|
||||
"inputSize": 1,
|
||||
"outputSize": 1,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"address": "address",
|
||||
"height": 1,
|
||||
"confirmations": 1,
|
||||
"aliens": [],
|
||||
"subType": "mint",
|
||||
"isCancelled": false,
|
||||
"slateId": "slateId",
|
||||
"otherData": "otherData",
|
||||
}
|
||||
]
|
||||
});
|
||||
final txdata =
|
||||
TransactionData.fromJson({"dateTimeChunks": [], "txChunks": []});
|
||||
txdata.findTransaction("txid");
|
||||
txdata.getAllTransactions();
|
||||
});
|
||||
});
|
||||
|
||||
group("Timestamp", () {
|
||||
test("Timestamp is now", () {
|
||||
final date = extractDateFromTimestamp(0);
|
||||
});
|
||||
|
||||
test("Timestamp is null", () {
|
||||
final date = extractDateFromTimestamp(null);
|
||||
});
|
||||
|
||||
test("Timestamp is a random date", () {
|
||||
final date = extractDateFromTimestamp(1876352482);
|
||||
});
|
||||
});
|
||||
|
||||
group("Transaction", () {
|
||||
test("Transaction from Json", () {
|
||||
final tx = Transaction.fromJson({
|
||||
"txid": "txid",
|
||||
"confirmed_status": true,
|
||||
"timestamp": 1876352482,
|
||||
"txType": "txType",
|
||||
"amount": 10,
|
||||
"worthNow": "1",
|
||||
"worthAtBlockTimestamp": "1",
|
||||
"fees": 1,
|
||||
"inputSize": 1,
|
||||
"outputSize": 1,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"address": "address",
|
||||
"height": 1,
|
||||
"confirmations": 1,
|
||||
"aliens": [],
|
||||
"subType": "mint",
|
||||
"isCancelled": false,
|
||||
"slateId": "slateId",
|
||||
"otherData": "otherData",
|
||||
});
|
||||
});
|
||||
|
||||
test("Transaction from Lelantus Json", () {
|
||||
final tx = Transaction.fromLelantusJson({
|
||||
"txid": "txid",
|
||||
"confirmed_status": true,
|
||||
"timestamp": 1876352482,
|
||||
"txType": "txType",
|
||||
"amount": 10,
|
||||
"worthNow": "1",
|
||||
"worthAtBlockTimestamp": "1",
|
||||
"fees": 1,
|
||||
"inputSize": 1,
|
||||
"outputSize": 1,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"address": "address",
|
||||
"height": 1,
|
||||
"confirmations": 1,
|
||||
"aliens": [],
|
||||
"subType": "mint",
|
||||
"isCancelled": false,
|
||||
"slateId": "slateId",
|
||||
"otherData": "otherData",
|
||||
});
|
||||
});
|
||||
|
||||
test("TransactionChunk", () {
|
||||
final transactionchunk = TransactionChunk.fromJson({
|
||||
"timestamp": 45920,
|
||||
"transactions": [],
|
||||
});
|
||||
expect(
|
||||
transactionchunk.toString(), "timestamp: 45920 transactions: [\n]");
|
||||
});
|
||||
|
||||
test("TransactionChunk with a transaction", () {
|
||||
final txChunk = TransactionChunk.fromJson({
|
||||
"timestamp": 993260735,
|
||||
"transactions": [
|
||||
{
|
||||
"txid": "txid",
|
||||
"confirmed_status": true,
|
||||
"timestamp": 1876352482,
|
||||
"txType": "txType",
|
||||
"amount": 10,
|
||||
"worthNow": "1",
|
||||
"worthAtBlockTimestamp": "1",
|
||||
"fees": 1,
|
||||
"inputSize": 1,
|
||||
"outputSize": 1,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"address": "address",
|
||||
"height": 1,
|
||||
"confirmations": 1,
|
||||
"aliens": [],
|
||||
"subType": "mint",
|
||||
"isCancelled": false,
|
||||
"slateId": "slateId",
|
||||
"otherData": "otherData",
|
||||
}
|
||||
]
|
||||
});
|
||||
expect(txChunk.toString(),
|
||||
"timestamp: 993260735 transactions: [\n {txid: txid, type: txType, subType: mint, value: 10, fee: 1, height: 1, confirm: true, confirmations: 1, address: address, timestamp: 1876352482, worthNow: 1, inputs: [], slateid: slateId } \n]");
|
||||
});
|
||||
});
|
||||
|
||||
group("Transaction isMinting", () {
|
||||
test("Transaction isMinting unconfirmed mint", () {
|
||||
final tx = Transaction(
|
||||
|
@ -94,4 +239,57 @@ void main() {
|
|||
expect(tx1 == tx2, false);
|
||||
expect(tx2.toString(), tx1.toString());
|
||||
});
|
||||
|
||||
group("Input", () {
|
||||
test("Input.toString", () {
|
||||
final input = Input(
|
||||
txid: "txid",
|
||||
vout: 1,
|
||||
prevout: null,
|
||||
scriptsig: "scriptsig",
|
||||
scriptsigAsm: "scriptsigAsm",
|
||||
witness: [],
|
||||
isCoinbase: false,
|
||||
sequence: 1,
|
||||
innerRedeemscriptAsm: "innerRedeemscriptAsm",
|
||||
); //Input
|
||||
|
||||
expect(input.toString(), "{txid: txid}");
|
||||
});
|
||||
|
||||
test("Input.toString", () {
|
||||
final input = Input.fromJson({
|
||||
"txid": "txid",
|
||||
"vout": 1,
|
||||
"prevout": null,
|
||||
"scriptSig": {"hex": "somehexString", "asm": "someasmthing"},
|
||||
"scriptsigAsm": "scriptsigAsm",
|
||||
"witness": [],
|
||||
"isCoinbase": false,
|
||||
"sequence": 1,
|
||||
"innerRedeemscriptAsm": "innerRedeemscriptAsm",
|
||||
}); //Input
|
||||
|
||||
expect(input.toString(), "{txid: txid}");
|
||||
});
|
||||
});
|
||||
|
||||
group("Output", () {
|
||||
test("Output.toString", () {
|
||||
final output = Output.fromJson({
|
||||
"scriptPubKey": {
|
||||
"hex": "somehexSting",
|
||||
"asm": "someasmthing",
|
||||
"type": "sometype",
|
||||
"addresses": "someaddresses",
|
||||
},
|
||||
"scriptpubkeyAsm": "scriptpubkeyAsm",
|
||||
"scriptpubkeyType": "scriptpubkeyType",
|
||||
"scriptpubkeyAddress": "address",
|
||||
"value": 2,
|
||||
}); //Input
|
||||
|
||||
expect(output.toString(), "Instance of \'Output\'");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenAnswer((_) async => Response(
|
||||
|
@ -36,10 +36,10 @@ void main() {
|
|||
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(price.toString(),
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
verify(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {'Content-Type': 'application/json'})).called(1);
|
||||
|
||||
verifyNoMoreInteractions(client);
|
||||
|
@ -50,7 +50,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenAnswer((_) async => Response(
|
||||
|
@ -68,12 +68,12 @@ void main() {
|
|||
await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(cachedPrice.toString(),
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [1, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0.00000315, -2.68533], Coin.epicCash: [0.00002803, 7.27524], Coin.firo: [0.0001096, -0.89304], Coin.monero: [0.00717236, -0.77656], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
|
||||
// verify only called once during filling of cache
|
||||
verify(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {'Content-Type': 'application/json'})).called(1);
|
||||
|
||||
verifyNoMoreInteractions(client);
|
||||
|
@ -84,7 +84,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenAnswer((_) async => Response(
|
||||
|
@ -97,7 +97,7 @@ void main() {
|
|||
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(price.toString(),
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
});
|
||||
|
||||
test("no internet available", () async {
|
||||
|
@ -105,7 +105,7 @@ void main() {
|
|||
|
||||
when(client.get(
|
||||
Uri.parse(
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=monero,bitcoin,epic-cash,zcoin,dogecoin,bitcoin-cash,namecoin,wownero&order=market_cap_desc&per_page=10&page=1&sparkline=false"),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
})).thenThrow(const SocketException(
|
||||
|
@ -117,7 +117,7 @@ void main() {
|
|||
final price = await priceAPI.getPricesAnd24hChange(baseCurrency: "btc");
|
||||
|
||||
expect(price.toString(),
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
'{Coin.bitcoin: [0, 0.0], Coin.bitcoincash: [0, 0.0], Coin.dogecoin: [0, 0.0], Coin.epicCash: [0, 0.0], Coin.firo: [0, 0.0], Coin.monero: [0, 0.0], Coin.wownero: [0, 0.0], Coin.namecoin: [0, 0.0], Coin.bitcoinTestNet: [0, 0.0], Coin.bitcoincashTestnet: [0, 0.0], Coin.dogecoinTestNet: [0, 0.0], Coin.firoTestNet: [0, 0.0]}');
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:bitcoindart/bitcoindart.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:bitcoindart/bitcoindart.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
|
Loading…
Reference in a new issue