Merge pull request #122 from cypherstack/staging

namecoin fix and other small ui fixes
This commit is contained in:
Rylee Davis 2022-10-05 21:53:43 -06:00 committed by GitHub
commit 1de599caa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 6124 additions and 5260 deletions

View file

@ -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/

3
assets/svg/Polygon.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="8" height="11" viewBox="0 0 8 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.83473 4.89146C7.24557 5.21575 7.24557 5.78425 6.83473 6.10854L1.53567 10.2914C0.941523 10.7604 -3.48066e-08 10.3873 0 9.68284L4.13348e-07 1.31716C4.48155e-07 0.612725 0.941523 0.239629 1.53567 0.708624L6.83473 4.89146Z" fill="#0056D2"/>
</svg>

After

Width:  |  Height:  |  Size: 350 B

11
assets/svg/box-auto.svg Normal file
View file

@ -0,0 +1,11 @@
<svg width="26" height="22" viewBox="0 0 26 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5432_15256)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.1875 1.8125H1.8125C1.08693 1.8125 0.5 2.39943 0.5 3.125V5.09375C0.5 5.45469 0.79482 5.75 1.15625 5.75H20.8438C21.2047 5.75 21.5 5.45469 21.5 5.09375V3.125C21.5 2.39943 20.9135 1.8125 20.1875 1.8125ZM1.8125 18.2188C1.8125 19.3057 2.69393 20.1875 3.78125 20.1875H14.145C13.1157 18.9035 12.5 17.2737 12.5 15.5C12.5 11.3579 15.8579 8 20 8C20.0627 8 20.1252 8.00077 20.1875 8.0023V7.0625H1.8125V18.2188ZM7.0625 10.1797C7.0625 9.90899 7.28398 9.6875 7.55469 9.6875H12.9C13.0933 9.6875 13.25 9.8442 13.25 10.0375V10.65C13.25 10.8433 13.0933 11 12.9 11H7.55469C7.28398 11 7.0625 10.7785 7.0625 10.5078V10.1797Z" fill="black"/>
<path d="M14 15.5C14 12.1859 16.6859 9.5 20 9.5C23.3141 9.5 26 12.1859 26 15.5C26 18.8141 23.3141 21.5 20 21.5C16.6859 21.5 14 18.8141 14 15.5ZM22.7141 14.4641C22.9695 14.2086 22.9695 13.7914 22.7141 13.5359C22.4586 13.2805 22.0414 13.2805 21.7859 13.5359L19.25 16.0719L18.2141 15.0359C17.9586 14.7805 17.5414 14.7805 17.2859 15.0359C17.0305 15.2914 17.0305 15.7086 17.2859 15.9641L18.7859 17.4641C19.0414 17.7195 19.4586 17.7195 19.7141 17.4641L22.7141 14.4641Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_5432_15256">
<rect width="25.5" height="21" fill="white" transform="translate(0.5 0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -1 +1 @@
Subproject commit 8e3afd002968d21a3de788569356587a70818022
Subproject commit f74f31e2f3b4a7c11907ae5df6cd38505cd25897

View file

@ -42,6 +42,25 @@ PODS:
- Flutter
- cw_shared_external/Sodium (0.0.1):
- Flutter
- cw_wownero (0.0.2):
- cw_shared_external
- cw_wownero/Boost (= 0.0.2)
- cw_wownero/OpenSSL (= 0.0.2)
- cw_wownero/Sodium (= 0.0.2)
- cw_wownero/Wownero (= 0.0.2)
- Flutter
- cw_wownero/Boost (0.0.2):
- cw_shared_external
- Flutter
- cw_wownero/OpenSSL (0.0.2):
- cw_shared_external
- Flutter
- cw_wownero/Sodium (0.0.2):
- cw_shared_external
- Flutter
- cw_wownero/Wownero (0.0.2):
- cw_shared_external
- Flutter
- devicelocale (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.4):
@ -127,6 +146,7 @@ DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
- cw_wownero (from `.symlinks/plugins/cw_wownero/ios`)
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
@ -169,6 +189,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/cw_monero/ios"
cw_shared_external:
:path: ".symlinks/plugins/cw_shared_external/ios"
cw_wownero:
:path: ".symlinks/plugins/cw_wownero/ios"
devicelocale:
:path: ".symlinks/plugins/devicelocale/ios"
file_picker:
@ -216,6 +238,7 @@ SPEC CHECKSUMS:
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
cw_wownero: 08e5713fe311a3be95efd7f3c1bf9d47d9cfafde
devicelocale: b22617f40038496deffba44747101255cee005b0
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179

View file

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
@ -253,6 +253,7 @@
"${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework",
"${BUILT_PRODUCTS_DIR}/cw_monero/cw_monero.framework",
"${BUILT_PRODUCTS_DIR}/cw_shared_external/cw_shared_external.framework",
"${BUILT_PRODUCTS_DIR}/cw_wownero/cw_wownero.framework",
"${BUILT_PRODUCTS_DIR}/devicelocale/devicelocale.framework",
"${BUILT_PRODUCTS_DIR}/file_picker/file_picker.framework",
"${BUILT_PRODUCTS_DIR}/flutter_libmonero/flutter_libmonero.framework",
@ -285,6 +286,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_monero.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_shared_external.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cw_wownero.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/devicelocale.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_picker.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_libmonero.framework",

View file

@ -84,6 +84,10 @@ 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);
if (!(Logging.isArmLinux || Logging.isTestEnv)) {
final isar = await Isar.open(

View file

@ -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: [

View file

@ -393,13 +393,15 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
padding: const EdgeInsets.all(4),
child: SvgPicture.asset(Assets.svg.pencil,
width: 12,
height: 12,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
padding: const EdgeInsets.all(6),
child: SvgPicture.asset(
Assets.svg.pencil,
width: 14,
height: 14,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
const SizedBox(
@ -421,13 +423,15 @@ class _ContactDetailsViewState extends ConsumerState<ContactDetailsView> {
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
padding: const EdgeInsets.all(4),
child: SvgPicture.asset(Assets.svg.copy,
width: 12,
height: 12,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
padding: const EdgeInsets.all(6),
child: SvgPicture.asset(
Assets.svg.copy,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
],

View file

@ -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,
@ -268,11 +272,11 @@ class ContactPopUp extends ConsumerWidget {
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
padding: const EdgeInsets.all(4),
padding: const EdgeInsets.all(6),
child: SvgPicture.asset(
Assets.svg.copy,
width: 12,
height: 12,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
@ -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)

View 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],
),
],
),
)
],
),
),
),
);
},
),
),
);
}
}

View file

@ -132,6 +132,7 @@ class _ConfirmChangeNowSendViewState
final managerProvider = ref.watch(walletsChangeNotifierProvider
.select((value) => value.getManagerProvider(walletId)));
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
leading: AppBarBackButton(
@ -327,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(
@ -341,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,
),
],

View file

@ -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);

View file

@ -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,

View file

@ -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 = "";
}
},

View file

@ -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 ? Constants.satsPerCoinMonero : coin == Coin.wownero ? Constants.satsPerCoinWownero : Constants.satsPerCoin)} ${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,59 +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
? Constants.satsPerCoinMonero
: coin == Coin.wownero
? Constants.satsPerCoinWownero
: Constants.satsPerCoin,
)} ${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),
);
}
},
),
],
),
),
],
),

View file

@ -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,
),
);
},
),
],
),
),

View file

@ -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);

View file

@ -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',
),
);
}
}
}
},

View file

@ -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 = "";

View file

@ -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 = "";

View file

@ -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 = "";

View file

@ -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);
}
}
}

View file

@ -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 {

View file

@ -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: [

View file

@ -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,
),
);
}
},
),
);

View file

@ -96,7 +96,14 @@ class AddWalletButton extends StatelessWidget {
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
Navigator.of(context).pushNamed(AddWalletView.routeName);
if (isDesktop) {
Navigator.of(
context,
rootNavigator: true,
).pushNamed(AddWalletView.routeName);
} else {
Navigator.of(context).pushNamed(AddWalletView.routeName);
}
},
child: Center(
child: Container(

View file

@ -0,0 +1,20 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SettingsView extends ConsumerStatefulWidget {
const SettingsView({Key? key}) : super(key: key);
static const String routeName = "/settingsView";
@override
ConsumerState<SettingsView> createState() => _SettingsView();
}
class _SettingsView extends ConsumerState<SettingsView> {
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
// TODO: implement build
throw UnimplementedError();
}
}

View file

@ -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(

View file

@ -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';

View file

@ -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');

View file

@ -2501,7 +2501,8 @@ class NamecoinWallet extends CoinServiceAPI {
int totalOutput = 0;
for (final output in txObject["vout"] as List) {
final address = output["scriptPubKey"]["addresses"][0];
Logging.instance.log(output, level: LogLevel.Info);
final address = output["scriptPubKey"]["address"];
final value = output["value"];
final _value = (Decimal.parse(value.toString()) *
Decimal.fromInt(Constants.satsPerCoin))

View file

@ -913,6 +913,9 @@ class WowneroWallet 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');

View file

@ -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;
}
}

View file

@ -51,7 +51,9 @@ class _SVG {
String txExchangeFailed(BuildContext context) =>
"assets/svg/${Theme.of(context).extension<StackColors>()!.themeType.name}/tx-exchange-icon-failed.svg";
String get polygon => "assets/svg/Polygon.svg";
String get drd => "assets/svg/drd-icon.svg";
String get boxAuto => "assets/svg/box-auto.svg";
String get plus => "assets/svg/plus.svg";
String get gear => "assets/svg/gear.svg";
String get bell => "assets/svg/bell.svg";

View file

@ -21,6 +21,8 @@ abstract class Constants {
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);

View file

@ -698,6 +698,25 @@ class STextStyles {
}
}
static TextStyle desktopTextExtraExtraSmall(BuildContext context) {
switch (_theme(context).themeType) {
case ThemeType.light:
return GoogleFonts.inter(
color: _theme(context).textSubtitle1,
fontWeight: FontWeight.w500,
fontSize: 14,
height: 21 / 14,
);
case ThemeType.dark:
return GoogleFonts.inter(
color: _theme(context).textSubtitle1,
fontWeight: FontWeight.w500,
fontSize: 14,
height: 21 / 14,
);
}
}
static TextStyle desktopButtonSmallSecondaryEnabled(BuildContext context) {
switch (_theme(context).themeType) {
case ThemeType.light:

View file

@ -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)

View file

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

View file

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"

40
macos/Podfile Normal file
View file

@ -0,0 +1,40 @@
platform :osx, '10.11'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_macos_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end

View file

@ -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.52+68
version: 1.5.0+70
environment:
sdk: ">=2.17.0 <3.0.0"
@ -289,6 +289,7 @@ flutter:
- assets/svg/tx-icon-anonymize.svg
- assets/svg/tx-icon-anonymize-pending.svg
- assets/svg/tx-icon-anonymize-failed.svg
- assets/svg/Polygon.svg
# coin icons
- assets/svg/coin_icons/Bitcoin.svg
- assets/svg/coin_icons/Bitcoincash.svg
@ -312,6 +313,7 @@ flutter:
- assets/svg/exchange-3.svg
- assets/svg/message-question-1.svg
- assets/svg/drd-icon.svg
- assets/svg/box-auto.svg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.

6
scripts/prebuild.sh Normal file
View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff