diff --git a/.gitignore b/.gitignore
index 0a8db4dcc3..f373c61fe1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -85,6 +85,7 @@ pubspec.yaml
/android/app/src/main/profile/AndroidManifest.xml
/android/app/src/main/kotlin/com/cypherstack/stackwallet/MainActivity.kt
/android/app/src/main/res/**/ic_launcher.png
+/android/app/src/main/res/**/splash.png
/ios/Runner/Info.plist
/ios/Runner.xcodeproj/project.pbxproj
@@ -122,3 +123,6 @@ lib/wl_gen/generated/
/linux/flutter/generated_plugins.cmake
/windows/flutter/generated_plugins.cmake
/macos/Flutter/GeneratedPluginRegistrant.swift
+
+/assets/windows/mwebd.exe
+/tool/build
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 1252b9a85b..f63ed52ef1 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -94,6 +94,7 @@ linter:
constant_identifier_names: false
prefer_final_locals: true
prefer_final_in_for_each: true
+ lines_longer_than_80_chars: true
# require_trailing_commas: true // causes issues with dart 3.7
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index 6a7964eae8..e1c48c38c0 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -32,4 +32,9 @@
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
# required for flutter file_picker
--keep class androidx.lifecycle.DefaultLifecycleObserver
\ No newline at end of file
+-keep class androidx.lifecycle.DefaultLifecycleObserver
+
+# required for flutter_secure_storage
+-dontwarn com.google.errorprone.annotations.**
+-dontwarn javax.annotation.Nullable
+-dontwarn javax.annotation.concurrent.GuardedBy
diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png
deleted file mode 100644
index b57b77cf00..0000000000
Binary files a/android/app/src/main/res/drawable-hdpi/splash.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png
deleted file mode 100644
index 47903b9092..0000000000
Binary files a/android/app/src/main/res/drawable-mdpi/splash.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-night-v21/background.png b/android/app/src/main/res/drawable-night-v21/background.png
new file mode 100644
index 0000000000..5596c666ea
Binary files /dev/null and b/android/app/src/main/res/drawable-night-v21/background.png differ
diff --git a/android/app/src/main/res/drawable-night-v21/launch_background.xml b/android/app/src/main/res/drawable-night-v21/launch_background.xml
new file mode 100644
index 0000000000..3cc4948a14
--- /dev/null
+++ b/android/app/src/main/res/drawable-night-v21/launch_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+ -
+
+
+
diff --git a/android/app/src/main/res/drawable-night/background.png b/android/app/src/main/res/drawable-night/background.png
new file mode 100644
index 0000000000..5596c666ea
Binary files /dev/null and b/android/app/src/main/res/drawable-night/background.png differ
diff --git a/android/app/src/main/res/drawable-night/launch_background.xml b/android/app/src/main/res/drawable-night/launch_background.xml
new file mode 100644
index 0000000000..3cc4948a14
--- /dev/null
+++ b/android/app/src/main/res/drawable-night/launch_background.xml
@@ -0,0 +1,9 @@
+
+
+ -
+
+
+ -
+
+
+
diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png
index 8a4950a508..60661e9a30 100644
Binary files a/android/app/src/main/res/drawable-v21/background.png and b/android/app/src/main/res/drawable-v21/background.png differ
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
index 3fe6b2e882..3cc4948a14 100644
--- a/android/app/src/main/res/drawable-v21/launch_background.xml
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -6,4 +6,4 @@
-
-
\ No newline at end of file
+
diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png
deleted file mode 100644
index 863332e3c4..0000000000
Binary files a/android/app/src/main/res/drawable-xhdpi/splash.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png
deleted file mode 100644
index 17c2de611a..0000000000
Binary files a/android/app/src/main/res/drawable-xxhdpi/splash.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png
deleted file mode 100644
index 9b86db9d41..0000000000
Binary files a/android/app/src/main/res/drawable-xxxhdpi/splash.png and /dev/null differ
diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png
index 8a4950a508..60661e9a30 100644
Binary files a/android/app/src/main/res/drawable/background.png and b/android/app/src/main/res/drawable/background.png differ
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
index 3fe6b2e882..3cc4948a14 100644
--- a/android/app/src/main/res/drawable/launch_background.xml
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -6,4 +6,4 @@
-
-
\ No newline at end of file
+
diff --git a/android/app/src/main/res/raw/keep.xml b/android/app/src/main/res/raw/keep.xml
deleted file mode 100644
index 1d6c664db0..0000000000
--- a/android/app/src/main/res/raw/keep.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
\ No newline at end of file
diff --git a/android/app/src/main/res/values-night-v31/styles.xml b/android/app/src/main/res/values-night-v31/styles.xml
new file mode 100644
index 0000000000..640c7ab463
--- /dev/null
+++ b/android/app/src/main/res/values-night-v31/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000000..dbc9ea9f1b
--- /dev/null
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values-v31/styles.xml b/android/app/src/main/res/values-v31/styles.xml
index e02ab7e5e7..8ba5d9a0bc 100644
--- a/android/app/src/main/res/values-v31/styles.xml
+++ b/android/app/src/main/res/values-v31/styles.xml
@@ -2,10 +2,11 @@
+
+
+
-
\ No newline at end of file
+
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index afa1e8eb0a..e4ef43fb98 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
diff --git a/android/settings.gradle b/android/settings.gradle
index 04c37e5f2b..ebf08564f2 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -18,7 +18,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
- id "com.android.application" version '8.7.0' apply false
+ id "com.android.application" version '8.11.1' apply false
id "org.jetbrains.kotlin.android" version "2.2.20" apply false
}
diff --git a/docs/building.md b/docs/building.md
index 6aa647e53b..41df95be09 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -4,7 +4,7 @@ Here you will find instructions on how to install the necessary tools for buildi
## Prerequisites
-- The only OS supported for building Android and Linux desktop is Ubuntu 20.04. Windows builds require using Ubuntu 20.04 on WSL2. macOS builds for itself and iOS. Advanced users may also be able to build on other Debian-based distributions like Linux Mint.
+- The only OS supported for building Android and Linux desktop is Ubuntu 24.04. Windows builds require using Ubuntu 24.04 on WSL2. macOS builds for itself and iOS. Advanced users may also be able to build on other Debian-based distributions like Linux Mint.
- Android setup ([Android Studio](https://developer.android.com/studio) and subsequent dependencies)
- 100 GB of storage
- Install go: [https://go.dev/doc/install](https://go.dev/doc/install)
@@ -77,12 +77,12 @@ pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3
```
### Flutter
-Install Flutter 3.29.2 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). You can also clone https://github.com/flutter/flutter, check out the `3.29.2` tag, and add its `flutter/bin` folder to your PATH as in
+Install Flutter 3.35.7 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). You can also clone https://github.com/flutter/flutter, check out the `3.35.7` tag, and add its `flutter/bin` folder to your PATH as in
```sh
FLUTTER_DIR="$HOME/development/flutter"
git clone https://github.com/flutter/flutter.git "$FLUTTER_DIR"
cd "$FLUTTER_DIR"
-git checkout 3.29.2
+git checkout 3.35.7
echo 'export PATH="$PATH:'"$FLUTTER_DIR"'/bin"' >> "$HOME/.profile"
source "$HOME/.profile"
flutter precache
@@ -165,6 +165,7 @@ cd scripts/windows
```
install go in WSL [https://go.dev/doc/install](https://go.dev/doc/install) (follow linux instructions) and ensure you have `x86_64-w64-mingw32-gcc`
+go version should be at least 1.24
and use `scripts/build_app.sh` to build plugins:
```
@@ -292,13 +293,13 @@ If the DLLs were built on the WSL filesystem instead of on Windows, copy the res
Frostdart will be built by the Windows host later.
### Install Flutter on Windows host
-Install Flutter 3.29.2 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/get-started/install/windows/desktop?tab=download#install-the-flutter-sdk) or by cloning https://github.com/flutter/flutter, checking out the `3.29.2` tag, and adding its `flutter/bin` folder to your PATH as in
+Install Flutter 3.35.7 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/get-started/install/windows/desktop?tab=download#install-the-flutter-sdk) or by cloning https://github.com/flutter/flutter, checking out the `3.35.7` tag, and adding its `flutter/bin` folder to your PATH as in
```bat
@echo off
set "FLUTTER_DIR=%USERPROFILE%\development\flutter"
git clone https://github.com/flutter/flutter.git "%FLUTTER_DIR%"
cd /d "%FLUTTER_DIR%"
-git checkout 3.29.2
+git checkout 3.35.7
setx PATH "%PATH%;%FLUTTER_DIR%\bin"
echo Flutter setup completed. Please restart your command prompt.
```
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
index 9f447e1b38..8bb185b107 100644
--- a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
+++ b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
@@ -2,16 +2,17 @@
"images" : [
{
"filename" : "background.png",
- "idiom" : "universal",
- "scale" : "1x"
+ "idiom" : "universal"
},
{
- "idiom" : "universal",
- "scale" : "2x"
- },
- {
- "idiom" : "universal",
- "scale" : "3x"
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "filename" : "darkbackground.png",
+ "idiom" : "universal"
}
],
"info" : {
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
index 8a4950a508..60661e9a30 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png
new file mode 100644
index 0000000000..5596c666ea
Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png differ
diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard
index 0430c335af..7aa6dfbc25 100644
--- a/ios/Runner/Base.lproj/LaunchScreen.storyboard
+++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -41,4 +41,4 @@
-
\ No newline at end of file
+
diff --git a/lib/app_config.dart b/lib/app_config.dart
index 3004413d2a..ab5d1da283 100644
--- a/lib/app_config.dart
+++ b/lib/app_config.dart
@@ -16,6 +16,8 @@ abstract class AppConfig {
static const emptyWalletsMessage = _emptyWalletsMessage;
+ static const windowsMwebdExeHash = _mwebdExeHash;
+
static String get appDefaultDataDirName => _appDataDirName;
static String get shortDescriptionText => _shortDescriptionText;
static String get commitHash => _commitHash;
diff --git a/lib/main.dart b/lib/main.dart
index 7b4d40fde8..dd35ae7b51 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -23,6 +23,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:keyboard_dismisser/keyboard_dismisser.dart';
import 'package:logger/logger.dart';
+import 'package:mobile_app_privacy/mobile_app_privacy.dart';
import 'package:path_provider/path_provider.dart';
import 'package:window_size/window_size.dart';
@@ -328,6 +329,10 @@ class _MaterialAppWithThemeState extends ConsumerState
with WidgetsBindingObserver {
static const platform = MethodChannel("STACK_WALLET_RESTORE");
+ final _mobileAppPrivacy = Platform.isAndroid || Platform.isIOS
+ ? MobileAppPrivacy()
+ : null;
+
// late final Wallets _wallets;
// late final Prefs _prefs;
late final NotificationsService _notificationsService;
@@ -459,6 +464,11 @@ class _MaterialAppWithThemeState extends ConsumerState
});
}
+ if (Platform.isAndroid &&
+ ref.read(prefsChangeNotifierProvider).disableScreenShots) {
+ unawaited(_mobileAppPrivacy?.setFlagSecure(true));
+ }
+
String themeId;
if (ref.read(prefsChangeNotifierProvider).enableSystemBrightness) {
final brightness = WidgetsBinding.instance.window.platformBrightness;
@@ -554,7 +564,18 @@ class _MaterialAppWithThemeState extends ConsumerState
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
debugPrint("didChangeAppLifecycleState: ${state.name}");
- if (state == AppLifecycleState.resumed) {}
+
+ if (state == AppLifecycleState.resumed) {
+ await _mobileAppPrivacy?.disableOverlay();
+ } else {
+ if (ref.read(prefsChangeNotifierProvider).privacyScreen) {
+ await _mobileAppPrivacy?.enableOverlay(
+ color: ref.read(themeProvider).popupBG, // only android, ios uses blur
+ blurInsteadOfColor: true, // ignored on android
+ );
+ }
+ }
+
switch (state) {
case AppLifecycleState.inactive:
break;
@@ -610,7 +631,10 @@ class _MaterialAppWithThemeState extends ConsumerState
@override
Future didRequestAppExit() async {
debugPrint("didRequestAppExit called");
- if (Platform.isMacOS) {
+ if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
+ // Monero will cause app to stop responding if in the middle of doing
+ // things like a scan on the c++ side of things.
+
// On macOS, mwebd fails to shut down, hanging the app on close.
//
// Exiting is a hack fix for this issue.
@@ -691,6 +715,13 @@ class _MaterialAppWithThemeState extends ConsumerState
// addToDebugMessagesDB: false);
// });
+ if (Platform.isAndroid) {
+ ref.listen(
+ prefsChangeNotifierProvider.select((s) => s.disableScreenShots),
+ (_, next) => _mobileAppPrivacy?.setFlagSecure(next),
+ );
+ }
+
final colorScheme = ref.watch(colorProvider.state).state;
return MaterialApp(
diff --git a/lib/networking/http.dart b/lib/networking/http.dart
index 80ea57c370..48b5f1c661 100644
--- a/lib/networking/http.dart
+++ b/lib/networking/http.dart
@@ -20,27 +20,21 @@ class Response {
}
class HTTP {
+ const HTTP();
+
Future get({
required Uri url,
Map? headers,
- required ({
- InternetAddress host,
- int port,
- })? proxyInfo,
+ required ({InternetAddress host, int port})? proxyInfo,
}) async {
final httpClient = HttpClient();
try {
if (proxyInfo != null) {
SocksTCPClient.assignToHttpClient(httpClient, [
- ProxySettings(
- proxyInfo.host,
- proxyInfo.port,
- ),
+ ProxySettings(proxyInfo.host, proxyInfo.port),
]);
}
- final HttpClientRequest request = await httpClient.getUrl(
- url,
- );
+ final HttpClientRequest request = await httpClient.getUrl(url);
if (headers != null) {
headers.forEach((key, value) => request.headers.add(key, value));
@@ -48,10 +42,7 @@ class HTTP {
final response = await request.close();
- return Response(
- await _bodyBytes(response),
- response.statusCode,
- );
+ return Response(await _bodyBytes(response), response.statusCode);
} catch (e, s) {
Logging.instance.w("HTTP.get() rethrew: ", error: e, stackTrace: s);
rethrow;
@@ -65,24 +56,16 @@ class HTTP {
Map? headers,
Object? body,
Encoding? encoding,
- required ({
- InternetAddress host,
- int port,
- })? proxyInfo,
+ required ({InternetAddress host, int port})? proxyInfo,
}) async {
final httpClient = HttpClient();
try {
if (proxyInfo != null) {
SocksTCPClient.assignToHttpClient(httpClient, [
- ProxySettings(
- proxyInfo.host,
- proxyInfo.port,
- ),
+ ProxySettings(proxyInfo.host, proxyInfo.port),
]);
}
- final HttpClientRequest request = await httpClient.postUrl(
- url,
- );
+ final HttpClientRequest request = await httpClient.postUrl(url);
if (headers != null) {
headers.forEach((key, value) => request.headers.add(key, value));
@@ -91,10 +74,7 @@ class HTTP {
request.write(body);
final response = await request.close();
- return Response(
- await _bodyBytes(response),
- response.statusCode,
- );
+ return Response(await _bodyBytes(response), response.statusCode);
} catch (e, s) {
Logging.instance.w("HTTP.post() rethrew: ", error: e, stackTrace: s);
rethrow;
@@ -110,9 +90,7 @@ class HTTP {
(data) {
bytes.addAll(data);
},
- onDone: () => completer.complete(
- Uint8List.fromList(bytes),
- ),
+ onDone: () => completer.complete(Uint8List.fromList(bytes)),
onError: (Object err, StackTrace s) => Logging.instance.e(
"Http wrapper layer listen",
error: err,
diff --git a/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart b/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart
index 33eddc54b8..0c756d0828 100644
--- a/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart
+++ b/lib/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart
@@ -215,8 +215,9 @@ class _RestoreFrostMsWalletViewState
}
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
- configFieldController.text = qrResult.rawContent;
+ configFieldController.text = qrResult.rawContent!;
setState(() {
_configEmpty = configFieldController.text.isEmpty;
diff --git a/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart b/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart
index bfe41c3feb..1e7e9b99cd 100644
--- a/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart
+++ b/lib/pages/add_wallet_views/new_wallet_options/new_wallet_options_view.dart
@@ -22,24 +22,23 @@ import '../../../widgets/desktop/desktop_scaffold.dart';
import '../../../widgets/desktop/primary_button.dart';
import '../../../widgets/rounded_white_container.dart';
import '../../../widgets/stack_text_field.dart';
+import '../../../widgets/toggle.dart';
import '../create_or_restore_wallet_view/sub_widgets/coin_image.dart';
import '../new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart';
import '../restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart';
import '../restore_wallet_view/sub_widgets/mnemonic_word_count_select_sheet.dart';
-final pNewWalletOptions = StateProvider<
- ({
- String mnemonicPassphrase,
- int mnemonicWordsCount,
- bool convertToViewOnly,
- })?>(
- (ref) => null,
-);
+final pNewWalletOptions =
+ StateProvider<
+ ({
+ String mnemonicPassphrase,
+ int mnemonicWordsCount,
+ bool convertToViewOnly,
+ bool convertToViewOnlySpark,
+ })?
+ >((ref) => null);
-enum NewWalletOptions {
- Default,
- Advanced;
-}
+enum NewWalletOptions { Default, Advanced }
class NewWalletOptionsView extends ConsumerStatefulWidget {
const NewWalletOptionsView({
@@ -66,6 +65,7 @@ class _NewWalletOptionsViewState extends ConsumerState {
NewWalletOptions _selectedOptions = NewWalletOptions.Default;
bool _convertToViewOnly = false;
+ bool _firoFlag = true;
@override
void initState() {
@@ -94,17 +94,15 @@ class _NewWalletOptionsViewState extends ConsumerState {
leading: AppBarBackButton(),
trailing: ExitToMyStackButton(),
),
- body: SizedBox(
- width: 480,
- child: child,
- ),
+ body: SizedBox(width: 480, child: child),
),
child: ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Background(
child: Scaffold(
- backgroundColor:
- Theme.of(context).extension()!.background,
+ backgroundColor: Theme.of(
+ context,
+ ).extension()!.background,
appBar: AppBar(
leading: const AppBarBackButton(),
title: Text(
@@ -135,20 +133,10 @@ class _NewWalletOptionsViewState extends ConsumerState {
),
child: Column(
children: [
- if (Util.isDesktop)
- const Spacer(
- flex: 10,
- ),
+ if (Util.isDesktop) const Spacer(flex: 10),
+ if (!Util.isDesktop) const SizedBox(height: 16),
if (!Util.isDesktop)
- const SizedBox(
- height: 16,
- ),
- if (!Util.isDesktop)
- CoinImage(
- coin: widget.coin,
- height: 100,
- width: 100,
- ),
+ CoinImage(coin: widget.coin, height: 100, width: 100),
if (Util.isDesktop)
Text(
"Wallet options",
@@ -157,9 +145,7 @@ class _NewWalletOptionsViewState extends ConsumerState {
? STextStyles.desktopH2(context)
: STextStyles.pageTitleH1(context),
),
- SizedBox(
- height: Util.isDesktop ? 32 : 16,
- ),
+ SizedBox(height: Util.isDesktop ? 32 : 16),
DropdownButtonHideUnderline(
child: DropdownButton2(
value: _selectedOptions,
@@ -187,34 +173,29 @@ class _NewWalletOptionsViewState extends ConsumerState {
Assets.svg.chevronDown,
width: 12,
height: 6,
- color: Theme.of(context)
- .extension()!
- .textFieldActiveSearchIconRight,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldActiveSearchIconRight,
),
),
dropdownStyleData: DropdownStyleData(
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color: Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
menuItemStyleData: const MenuItemStyleData(
- padding: EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 8,
- ),
+ padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
),
- const SizedBox(
- height: 24,
- ),
+ const SizedBox(height: 24),
if (_selectedOptions == NewWalletOptions.Advanced)
Column(
children: [
@@ -238,8 +219,9 @@ class _NewWalletOptionsViewState extends ConsumerState {
onChanged: (value) {
if (value is int) {
ref
- .read(mnemonicWordCountStateProvider.state)
- .state = value;
+ .read(mnemonicWordCountStateProvider.state)
+ .state =
+ value;
}
},
isExpanded: true,
@@ -257,9 +239,9 @@ class _NewWalletOptionsViewState extends ConsumerState {
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color: Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -293,30 +275,28 @@ class _NewWalletOptionsViewState extends ConsumerState {
},
),
if (widget.coin.hasMnemonicPassphraseSupport)
- const SizedBox(
- height: 24,
- ),
+ const SizedBox(height: 24),
if (widget.coin.hasMnemonicPassphraseSupport)
RoundedWhiteContainer(
child: Center(
child: Text(
"You may add a BIP39 passphrase. This is optional. "
- "You will need BOTH your seed and your passphrase to recover the wallet.",
+ "You will need BOTH your seed and your passphrase to "
+ "recover the wallet.",
style: Util.isDesktop
- ? STextStyles.desktopTextExtraSmall(context)
- .copyWith(
- color: Theme.of(context)
- .extension()!
- .textSubtitle1,
+ ? STextStyles.desktopTextExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
+ context,
+ ).extension()!.textSubtitle1,
)
: STextStyles.itemSubtitle(context),
),
),
),
if (widget.coin.hasMnemonicPassphraseSupport)
- const SizedBox(
- height: 8,
- ),
+ const SizedBox(height: 8),
if (widget.coin.hasMnemonicPassphraseSupport)
ClipRRect(
borderRadius: BorderRadius.circular(
@@ -327,89 +307,121 @@ class _NewWalletOptionsViewState extends ConsumerState {
focusNode: passwordFocusNode,
controller: passwordController,
style: Util.isDesktop
- ? STextStyles.desktopTextMedium(context).copyWith(
- height: 2,
- )
+ ? STextStyles.desktopTextMedium(
+ context,
+ ).copyWith(height: 2)
: STextStyles.field(context),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
- decoration: standardInputDecoration(
- "BIP39 passphrase",
- passwordFocusNode,
- context,
- ).copyWith(
- suffixIcon: UnconstrainedBox(
- child: ConditionalParent(
- condition: Util.isDesktop,
- builder: (child) => SizedBox(
- height: 70,
- child: child,
- ),
- child: Row(
- children: [
- SizedBox(
- width: Util.isDesktop ? 24 : 16,
- ),
- GestureDetector(
- key: const Key(
- "mnemonicPassphraseFieldShowPasswordButtonKey",
- ),
- onTap: () async {
- setState(() {
- hidePassword = !hidePassword;
- });
- },
- child: SvgPicture.asset(
- hidePassword
- ? Assets.svg.eye
- : Assets.svg.eyeSlash,
- color: Theme.of(context)
- .extension()!
- .textDark3,
- width: Util.isDesktop ? 24 : 16,
- height: Util.isDesktop ? 24 : 16,
- ),
+ decoration:
+ standardInputDecoration(
+ "BIP39 passphrase",
+ passwordFocusNode,
+ context,
+ ).copyWith(
+ suffixIcon: UnconstrainedBox(
+ child: ConditionalParent(
+ condition: Util.isDesktop,
+ builder: (child) =>
+ SizedBox(height: 70, child: child),
+ child: Row(
+ children: [
+ SizedBox(width: Util.isDesktop ? 24 : 16),
+ GestureDetector(
+ key: const Key(
+ "mnemonicPassphraseFieldShowPasswordButtonKey",
+ ),
+ onTap: () async {
+ setState(() {
+ hidePassword = !hidePassword;
+ });
+ },
+ child: SvgPicture.asset(
+ hidePassword
+ ? Assets.svg.eye
+ : Assets.svg.eyeSlash,
+ color: Theme.of(
+ context,
+ ).extension()!.textDark3,
+ width: Util.isDesktop ? 24 : 16,
+ height: Util.isDesktop ? 24 : 16,
+ ),
+ ),
+ const SizedBox(width: 12),
+ ],
),
- const SizedBox(
- width: 12,
- ),
- ],
+ ),
),
),
- ),
- ),
),
),
if (widget.coin is ViewOnlyOptionCurrencyInterface)
- const SizedBox(
- height: 24,
- ),
+ const SizedBox(height: 24),
if (widget.coin is ViewOnlyOptionCurrencyInterface)
CheckboxTextButton(
- label: "Convert to view only wallet. "
+ label:
+ "Convert to view only wallet. "
"You will only be shown the seed phrase once. "
"Save it somewhere. "
- "If you lose it you will lose access to any funds in this wallet.",
+ "If you lose it you will lose access to any funds in"
+ " this wallet.",
onChanged: (value) {
_convertToViewOnly = value;
+ if (mounted && widget.coin is Firo) {
+ setState(() {});
+ }
},
),
+ if (_convertToViewOnly &&
+ widget.coin is ViewOnlyOptionCurrencyInterface &&
+ widget.coin is Firo)
+ const SizedBox(height: 24),
+ if (_convertToViewOnly &&
+ widget.coin is ViewOnlyOptionCurrencyInterface &&
+ widget.coin is Firo)
+ SizedBox(
+ height: 48,
+ child: Toggle(
+ key: UniqueKey(),
+ onColor: Theme.of(
+ context,
+ ).extension()!.popupBG,
+ offColor: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
+ onText: "Spark",
+ offText: "XPub",
+ isOn: !_firoFlag,
+ onValueChanged: (value) {
+ FocusManager.instance.primaryFocus?.unfocus();
+ setState(() {
+ _firoFlag = !value;
+ });
+ },
+ decoration: BoxDecoration(
+ color: Colors.transparent,
+ borderRadius: BorderRadius.circular(
+ Constants.size.circularBorderRadius,
+ ),
+ ),
+ ),
+ ),
],
),
if (!Util.isDesktop) const Spacer(),
- SizedBox(
- height: Util.isDesktop ? 32 : 16,
- ),
+ SizedBox(height: Util.isDesktop ? 32 : 16),
PrimaryButton(
label: "Continue",
onPressed: () {
if (_selectedOptions == NewWalletOptions.Advanced) {
ref.read(pNewWalletOptions.notifier).state = (
- mnemonicWordsCount:
- ref.read(mnemonicWordCountStateProvider.state).state,
+ mnemonicWordsCount: ref
+ .read(mnemonicWordCountStateProvider.state)
+ .state,
mnemonicPassphrase: passwordController.text,
convertToViewOnly: _convertToViewOnly,
+ convertToViewOnlySpark: _convertToViewOnly && _firoFlag,
);
} else {
ref.read(pNewWalletOptions.notifier).state = null;
@@ -417,21 +429,12 @@ class _NewWalletOptionsViewState extends ConsumerState {
Navigator.of(context).pushNamed(
NewWalletRecoveryPhraseWarningView.routeName,
- arguments: Tuple2(
- widget.walletName,
- widget.coin,
- ),
+ arguments: Tuple2(widget.walletName, widget.coin),
);
},
),
- if (!Util.isDesktop)
- const SizedBox(
- height: 16,
- ),
- if (Util.isDesktop)
- const Spacer(
- flex: 15,
- ),
+ if (!Util.isDesktop) const SizedBox(height: 16),
+ if (Util.isDesktop) const Spacer(flex: 15),
],
),
),
diff --git a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart
index 031a8b41de..52a78a975f 100644
--- a/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart
+++ b/lib/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart
@@ -199,7 +199,9 @@ class _NewWalletRecoveryPhraseWarningViewState
.mnemonicPassphrase;
} else {
// this may not be epiccash and sol specific?
- if (coin is Epiccash || coin is Solana) {
+ if (coin is Epiccash ||
+ coin is Mimblewimblecoin ||
+ coin is Solana) {
mnemonicPassphrase = "";
}
}
diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart
index e4709aada9..4dd084f8c2 100644
--- a/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart
+++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_view_only_wallet_view.dart
@@ -26,23 +26,21 @@ import '../../../wallets/crypto_currency/intermediate/cryptonote_currency.dart';
import '../../../wallets/isar/models/wallet_info.dart';
import '../../../wallets/wallet/impl/epiccash_wallet.dart';
import '../../../wallets/wallet/impl/mimblewimblecoin_wallet.dart';
-import '../../../wallets/wallet/impl/monero_wallet.dart';
-import '../../../wallets/wallet/impl/wownero_wallet.dart';
import '../../../wallets/wallet/impl/xelis_wallet.dart';
+import '../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
import '../../../wallets/wallet/wallet.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../../widgets/desktop/desktop_app_bar.dart';
import '../../../widgets/desktop/desktop_scaffold.dart';
import '../../../widgets/desktop/primary_button.dart';
-import '../../../widgets/stack_text_field.dart';
import '../../../widgets/options.dart';
+import '../../../widgets/stack_text_field.dart';
import '../../home_view/home_view.dart';
import 'confirm_recovery_dialog.dart';
import 'sub_widgets/restore_failed_dialog.dart';
import 'sub_widgets/restore_succeeded_dialog.dart';
import 'sub_widgets/restoring_dialog.dart';
-import '../../../wallets/wallet/impl/firo_wallet.dart';
class RestoreViewOnlyWalletView extends ConsumerStatefulWidget {
const RestoreViewOnlyWalletView({
@@ -110,6 +108,7 @@ class _RestoreViewOnlyWalletViewState
ViewOnlyWalletType viewOnlyWalletType = _walletType;
if (widget.coin is Bip39HDCurrency) {
+ // already set above
} else if (widget.coin is CryptonoteCurrency) {
viewOnlyWalletType = ViewOnlyWalletType.cryptonote;
} else {
@@ -127,6 +126,7 @@ class _RestoreViewOnlyWalletViewState
name: widget.walletName,
restoreHeight: widget.restoreBlockHeight,
otherDataJsonString: jsonEncode(otherDataJson),
+ overrideAddressType: viewOnlyWalletType == .spark ? .spark : null,
);
bool isRestoring = true;
@@ -222,29 +222,21 @@ class _RestoreViewOnlyWalletViewState
);
// TODO: extract interface with isRestore param
- switch (wallet.runtimeType) {
- case const (EpiccashWallet):
- await (wallet as EpiccashWallet).init(isRestore: true);
- break;
-
- case const (MimblewimblecoinWallet):
- await (wallet as MimblewimblecoinWallet).init(isRestore: true);
- break;
-
- case const (MoneroWallet):
- await (wallet as MoneroWallet).init(isRestore: true);
+ switch (wallet) {
+ case EpiccashWallet():
+ await wallet.init(isRestore: true);
break;
- case const (WowneroWallet):
- await (wallet as WowneroWallet).init(isRestore: true);
+ case MimblewimblecoinWallet():
+ await wallet.init(isRestore: true);
break;
- case const (XelisWallet):
- await (wallet as XelisWallet).init(isRestore: true);
+ case CryptonoteWallet():
+ await wallet.init(isRestore: true);
break;
- case const (FiroWallet):
- await (wallet as FiroWallet).init();
+ case XelisWallet():
+ await wallet.init(isRestore: true);
break;
default:
@@ -352,28 +344,27 @@ class _RestoreViewOnlyWalletViewState
return MasterScaffold(
isDesktop: isDesktop,
- appBar:
- isDesktop
- ? const DesktopAppBar(
- isCompactHeight: false,
- leading: AppBarBackButton(),
- trailing: ExitToMyStackButton(),
- )
- : AppBar(
- leading: AppBarBackButton(
- onPressed: () async {
- if (FocusScope.of(context).hasFocus) {
- FocusScope.of(context).unfocus();
- await Future.delayed(
- const Duration(milliseconds: 50),
- );
- }
- if (context.mounted) {
- Navigator.of(context).pop();
- }
- },
- ),
+ appBar: isDesktop
+ ? const DesktopAppBar(
+ isCompactHeight: false,
+ leading: AppBarBackButton(),
+ trailing: ExitToMyStackButton(),
+ )
+ : AppBar(
+ leading: AppBarBackButton(
+ onPressed: () async {
+ if (FocusScope.of(context).hasFocus) {
+ FocusScope.of(context).unfocus();
+ await Future.delayed(
+ const Duration(milliseconds: 50),
+ );
+ }
+ if (context.mounted) {
+ Navigator.of(context).pop();
+ }
+ },
),
+ ),
body: Container(
color: Theme.of(context).extension()!.background,
child: LayoutBuilder(
@@ -398,10 +389,9 @@ class _RestoreViewOnlyWalletViewState
SizedBox(height: isDesktop ? 0 : 4),
Text(
"Enter view only details",
- style:
- isDesktop
- ? STextStyles.desktopH2(context)
- : STextStyles.pageTitleH1(context),
+ style: isDesktop
+ ? STextStyles.desktopH2(context)
+ : STextStyles.pageTitleH1(context),
),
if (isElectrumX) SizedBox(height: isDesktop ? 24 : 16),
if (isElectrumX)
@@ -414,18 +404,19 @@ class _RestoreViewOnlyWalletViewState
"Single address",
"Extended pub key",
if (widget.coin is Firo)
- isDesktop ? "Spark View Key" : "View Key"
+ isDesktop ? "Spark View Key" : "View Key",
],
- onColor: Theme.of(context)
- .extension()!
- .popupBG,
- offColor: Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
- selectedIndex: _walletType.index-1,
+ onColor: Theme.of(
+ context,
+ ).extension()!.popupBG,
+ offColor: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
+ selectedIndex: _walletType.index - 1,
onValueChanged: (value) {
setState(() {
- _walletType = ViewOnlyWalletType.values[value+1];
+ _walletType =
+ ViewOnlyWalletType.values[value + 1];
});
},
decoration: BoxDecoration(
@@ -436,10 +427,9 @@ class _RestoreViewOnlyWalletViewState
),
),
),
- SizedBox(
- height: isDesktop ? 24 : 16,
- ),
- if (!isElectrumX || _walletType == ViewOnlyWalletType.addressOnly)
+ SizedBox(height: isDesktop ? 24 : 16),
+ if (!isElectrumX ||
+ _walletType == ViewOnlyWalletType.addressOnly)
FullTextField(
key: const Key("viewOnlyAddressRestoreFieldKey"),
label: "Address",
@@ -459,11 +449,9 @@ class _RestoreViewOnlyWalletViewState
}
},
),
- if (!isElectrumX)
- SizedBox(
- height: isDesktop ? 16 : 12,
- ),
- if (isElectrumX && _walletType == ViewOnlyWalletType.xPub)
+ if (!isElectrumX) SizedBox(height: isDesktop ? 16 : 12),
+ if (isElectrumX &&
+ _walletType == ViewOnlyWalletType.xPub)
DropdownButtonHideUnderline(
child: DropdownButton2(
value: _currentDropDownValue,
@@ -490,10 +478,9 @@ class _RestoreViewOnlyWalletViewState
isExpanded: true,
buttonStyleData: ButtonStyleData(
decoration: BoxDecoration(
- color:
- Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -506,10 +493,9 @@ class _RestoreViewOnlyWalletViewState
Assets.svg.chevronDown,
width: 12,
height: 6,
- color:
- Theme.of(context)
- .extension()!
- .textFieldActiveSearchIconRight,
+ color: Theme.of(context)
+ .extension()!
+ .textFieldActiveSearchIconRight,
),
),
),
@@ -517,10 +503,9 @@ class _RestoreViewOnlyWalletViewState
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color:
- Theme.of(context)
- .extension()!
- .textFieldDefaultBG,
+ color: Theme.of(context)
+ .extension()!
+ .textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -534,11 +519,11 @@ class _RestoreViewOnlyWalletViewState
),
),
),
- if (isElectrumX && _walletType == ViewOnlyWalletType.xPub)
- SizedBox(
- height: isDesktop ? 16 : 12,
- ),
- if (!isElectrumX || _walletType == ViewOnlyWalletType.xPub)
+ if (isElectrumX &&
+ _walletType == ViewOnlyWalletType.xPub)
+ SizedBox(height: isDesktop ? 16 : 12),
+ if (!isElectrumX ||
+ _walletType == ViewOnlyWalletType.xPub)
FullTextField(
key: const Key("viewOnlyKeyRestoreFieldKey"),
label:
@@ -560,18 +545,18 @@ class _RestoreViewOnlyWalletViewState
},
),
if (_walletType == ViewOnlyWalletType.spark)
- SizedBox(
- height: isDesktop ? 16 : 12,
- ),
+ SizedBox(height: isDesktop ? 16 : 12),
if (_walletType == ViewOnlyWalletType.spark)
FullTextField(
- key: const Key("viewOnlySparkViewKeyRestoreFieldKey"),
+ key: const Key(
+ "viewOnlySparkViewKeyRestoreFieldKey",
+ ),
label: "Spark View Key",
controller: sparkViewKeyController,
onChanged: (value) {
- setState(() {
- _enableRestoreButton = value.isNotEmpty;
- });
+ setState(() {
+ _enableRestoreButton = value.isNotEmpty;
+ });
},
),
if (!isDesktop) const Spacer(),
diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart
index e87305fc24..55016b713a 100644
--- a/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart
+++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_wallet_view.dart
@@ -43,10 +43,8 @@ import '../../../wallets/crypto_currency/crypto_currency.dart';
import '../../../wallets/isar/models/wallet_info.dart';
import '../../../wallets/wallet/impl/epiccash_wallet.dart';
import '../../../wallets/wallet/impl/mimblewimblecoin_wallet.dart';
-import '../../../wallets/wallet/impl/monero_wallet.dart';
-import '../../../wallets/wallet/impl/salvium_wallet.dart';
-import '../../../wallets/wallet/impl/wownero_wallet.dart';
import '../../../wallets/wallet/impl/xelis_wallet.dart';
+import '../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
import '../../../wallets/wallet/intermediate/external_wallet.dart';
import '../../../wallets/wallet/supporting/epiccash_wallet_info_extension.dart';
import '../../../wallets/wallet/supporting/mimblewimblecoin_wallet_info_extension.dart';
@@ -343,35 +341,26 @@ class _RestoreWalletViewState extends ConsumerState {
);
// TODO: extract interface with isRestore param
- switch (wallet.runtimeType) {
- case const (EpiccashWallet):
- await (wallet as EpiccashWallet).init(isRestore: true);
+ switch (wallet) {
+ case EpiccashWallet():
+ await wallet.init(isRestore: true);
break;
- case const (MimblewimblecoinWallet):
- await (wallet as MimblewimblecoinWallet).init(isRestore: true);
+ case MimblewimblecoinWallet():
+ await wallet.init(isRestore: true);
break;
- case const (MoneroWallet):
- await (wallet as MoneroWallet).init(isRestore: true);
+ case CryptonoteWallet():
+ await wallet.init(isRestore: true);
break;
- case const (WowneroWallet):
- await (wallet as WowneroWallet).init(isRestore: true);
- break;
-
- case const (SalviumWallet):
- await (wallet as SalviumWallet).init(isRestore: true);
- break;
-
- case const (XelisWallet):
- await (wallet as XelisWallet).init(isRestore: true);
+ case XelisWallet():
+ await wallet.init(isRestore: true);
break;
default:
await wallet.init();
}
-
await wallet.recover(isRescan: false);
if (wallet is ExternalWallet) {
@@ -622,7 +611,7 @@ class _RestoreWalletViewState extends ConsumerState {
try {
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
- final results = AddressUtils.decodeQRSeedData(qrResult.rawContent);
+ final results = AddressUtils.decodeQRSeedData(qrResult.rawContent ?? "");
if (results["mnemonic"] != null) {
final list = (results["mnemonic"] as List)
@@ -872,6 +861,8 @@ class _RestoreWalletViewState extends ConsumerState {
child: Column(
children: [
TextFormField(
+ enableIMEPersonalizedLearning:
+ false,
obscureText: _hideSeedWords,
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
@@ -1018,6 +1009,8 @@ class _RestoreWalletViewState extends ConsumerState {
child: Column(
children: [
TextFormField(
+ enableIMEPersonalizedLearning:
+ false,
obscureText: _hideSeedWords,
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
@@ -1159,6 +1152,7 @@ class _RestoreWalletViewState extends ConsumerState {
vertical: 4,
),
child: TextFormField(
+ enableIMEPersonalizedLearning: false,
obscureText: _hideSeedWords,
autocorrect: !isDesktop,
enableSuggestions: !isDesktop,
diff --git a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
index e19eca1415..c00dab02e3 100644
--- a/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
+++ b/lib/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart
@@ -34,12 +34,12 @@ import '../../../wallets/crypto_currency/crypto_currency.dart';
import '../../../wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
import '../../../wallets/isar/models/wallet_info.dart';
import '../../../wallets/wallet/impl/epiccash_wallet.dart';
-import '../../../wallets/wallet/impl/monero_wallet.dart';
-import '../../../wallets/wallet/impl/wownero_wallet.dart';
+import '../../../wallets/wallet/impl/mimblewimblecoin_wallet.dart';
import '../../../wallets/wallet/impl/xelis_wallet.dart';
import '../../../wallets/wallet/intermediate/cryptonote_wallet.dart';
import '../../../wallets/wallet/wallet.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
+import '../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart';
import '../../../wallets/wallet/wallet_mixin_interfaces/view_only_option_interface.dart';
import '../../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../../widgets/desktop/desktop_app_bar.dart';
@@ -102,14 +102,16 @@ class _VerifyRecoveryPhraseViewState
return result == "verified";
}
- Future _convertToViewOnly() async {
+ Future _convertToViewOnly(bool firoSpark) async {
int height = 0;
final Map otherDataJson = {
WalletInfoKeys.isViewOnlyKey: true,
};
final ViewOnlyWalletType viewOnlyWalletType;
- if (widget.wallet is ExtendedKeysInterface) {
+ if (firoSpark) {
+ viewOnlyWalletType = .spark;
+ } else if (widget.wallet is ExtendedKeysInterface) {
viewOnlyWalletType = ViewOnlyWalletType.xPub;
} else if (widget.wallet is CryptonoteWallet) {
if (widget.wallet.cryptoCurrency is Monero) {
@@ -144,10 +146,18 @@ class _VerifyRecoveryPhraseViewState
name: widget.wallet.info.name,
restoreHeight: height,
otherDataJsonString: jsonEncode(otherDataJson),
+ overrideAddressType: viewOnlyWalletType == .spark ? .spark : null,
);
final ViewOnlyWalletData viewOnlyData;
- if (widget.wallet is ExtendedKeysInterface) {
+ if (viewOnlyWalletType == .spark) {
+ final sparkViewKey = (widget.wallet as SparkInterface).sparkViewKey;
+
+ viewOnlyData = SparkViewOnlyWalletData(
+ walletId: voInfo.walletId,
+ viewKey: sparkViewKey!,
+ );
+ } else if (widget.wallet is ExtendedKeysInterface) {
final extendedKeyInfo = await (widget.wallet as ExtendedKeysInterface)
.getXPubs();
final testPath = (_coin as Bip39HDCurrency).constructDerivePath(
@@ -204,21 +214,21 @@ class _VerifyRecoveryPhraseViewState
try {
// TODO: extract interface with isRestore param
- switch (voWallet.runtimeType) {
- case const (EpiccashWallet):
- await (voWallet as EpiccashWallet).init(isRestore: true);
+ switch (voWallet) {
+ case EpiccashWallet():
+ await voWallet.init(isRestore: true);
break;
- case const (MoneroWallet):
- await (voWallet as MoneroWallet).init(isRestore: true);
+ case MimblewimblecoinWallet():
+ await voWallet.init(isRestore: true);
break;
- case const (WowneroWallet):
- await (voWallet as WowneroWallet).init(isRestore: true);
+ case CryptonoteWallet():
+ await voWallet.init(isRestore: true);
break;
- case const (XelisWallet):
- await (voWallet as XelisWallet).init(isRestore: true);
+ case XelisWallet():
+ await voWallet.init(isRestore: true);
break;
default:
@@ -300,7 +310,9 @@ class _VerifyRecoveryPhraseViewState
try {
Exception? ex;
await showLoading(
- whileFuture: _convertToViewOnly(),
+ whileFuture: _convertToViewOnly(
+ ref.read(pNewWalletOptions)?.convertToViewOnlySpark == true,
+ ),
context: context,
message: "Converting to view only wallet",
rootNavigator: Util.isDesktop,
diff --git a/lib/pages/address_book_views/address_book_view.dart b/lib/pages/address_book_views/address_book_view.dart
index 64af275075..4a873cba66 100644
--- a/lib/pages/address_book_views/address_book_view.dart
+++ b/lib/pages/address_book_views/address_book_view.dart
@@ -64,8 +64,9 @@ class _AddressBookViewState extends ConsumerState {
final coins = [...AppConfig.coins];
coins.removeWhere((e) => e is Firo && e.network.isTestNet);
- final bool showTestNet =
- ref.read(prefsChangeNotifierProvider).showTestNetCoins;
+ final bool showTestNet = ref
+ .read(prefsChangeNotifierProvider)
+ .showTestNetCoins;
if (showTestNet) {
ref.read(addressBookFilterProvider).addAll(coins, false);
@@ -88,10 +89,7 @@ class _AddressBookViewState extends ConsumerState {
final String addressString;
if (wallet is SparkInterface) {
Address? address = await wallet.getCurrentReceivingSparkAddress();
- if (address == null) {
- address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).updateOrPutAddresses([address]);
- }
+ address ??= await wallet.generateNextSparkAddress(saveToDB: true);
addressString = address.value;
} else {
final address = await wallet.getCurrentReceivingAddress();
@@ -137,8 +135,9 @@ class _AddressBookViewState extends ConsumerState {
builder: (child) {
return Background(
child: Scaffold(
- backgroundColor:
- Theme.of(context).extension()!.background,
+ backgroundColor: Theme.of(
+ context,
+ ).extension()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () {
@@ -162,16 +161,14 @@ class _AddressBookViewState extends ConsumerState {
key: const Key("addressBookFilterViewButton"),
size: 36,
shadows: const [],
- color:
- Theme.of(
- context,
- ).extension()!.background,
+ color: Theme.of(
+ context,
+ ).extension()!.background,
icon: SvgPicture.asset(
Assets.svg.filter,
- color:
- Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension()!.accentColorDark,
width: 20,
height: 20,
),
@@ -195,16 +192,14 @@ class _AddressBookViewState extends ConsumerState {
key: const Key("addressBookAddNewContactViewButton"),
size: 36,
shadows: const [],
- color:
- Theme.of(
- context,
- ).extension()!.background,
+ color: Theme.of(
+ context,
+ ).extension()!.background,
icon: SvgPicture.asset(
Assets.svg.plus,
- color:
- Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension()!.accentColorDark,
width: 20,
height: 20,
),
@@ -260,38 +255,37 @@ class _AddressBookViewState extends ConsumerState {
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
- child:
- !isDesktop
- ? TextField(
- autocorrect: Util.isDesktop ? false : true,
- enableSuggestions: Util.isDesktop ? false : true,
- controller: _searchController,
- focusNode: _searchFocusNode,
- onChanged: (value) {
- setState(() {
- _searchTerm = value;
- });
- },
- style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Search",
- _searchFocusNode,
- context,
- ).copyWith(
- prefixIcon: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 10,
- vertical: 16,
- ),
- child: SvgPicture.asset(
- Assets.svg.search,
- width: 16,
- height: 16,
+ child: !isDesktop
+ ? TextField(
+ autocorrect: Util.isDesktop ? false : true,
+ enableSuggestions: Util.isDesktop ? false : true,
+ controller: _searchController,
+ focusNode: _searchFocusNode,
+ onChanged: (value) {
+ setState(() {
+ _searchTerm = value;
+ });
+ },
+ style: STextStyles.field(context),
+ decoration:
+ standardInputDecoration(
+ "Search",
+ _searchFocusNode,
+ context,
+ ).copyWith(
+ prefixIcon: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 10,
+ vertical: 16,
+ ),
+ child: SvgPicture.asset(
+ Assets.svg.search,
+ width: 16,
+ height: 16,
+ ),
),
- ),
- suffixIcon:
- _searchController.text.isNotEmpty
- ? Padding(
+ suffixIcon: _searchController.text.isNotEmpty
+ ? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
@@ -309,10 +303,10 @@ class _AddressBookViewState extends ConsumerState {
),
),
)
- : null,
- ),
- )
- : null,
+ : null,
+ ),
+ )
+ : null,
),
if (!isDesktop) const SizedBox(height: 16),
Text("Favorites", style: STextStyles.smallMed12(context)),
@@ -324,16 +318,15 @@ class _AddressBookViewState extends ConsumerState {
children: [
...contacts
.where(
- (element) =>
- element.addressesSorted
- .where(
- (e) => ref.watch(
- addressBookFilterProvider.select(
- (value) => value.coins.contains(e.coin),
- ),
- ),
- )
- .isNotEmpty,
+ (element) => element.addressesSorted
+ .where(
+ (e) => ref.watch(
+ addressBookFilterProvider.select(
+ (value) => value.coins.contains(e.coin),
+ ),
+ ),
+ )
+ .isNotEmpty,
)
.where(
(e) =>
@@ -375,17 +368,15 @@ class _AddressBookViewState extends ConsumerState {
children: [
...contacts
.where(
- (element) =>
- element.addressesSorted
- .where(
- (e) => ref.watch(
- addressBookFilterProvider.select(
- (value) =>
- value.coins.contains(e.coin),
- ),
- ),
- )
- .isNotEmpty,
+ (element) => element.addressesSorted
+ .where(
+ (e) => ref.watch(
+ addressBookFilterProvider.select(
+ (value) => value.coins.contains(e.coin),
+ ),
+ ),
+ )
+ .isNotEmpty,
)
.where(
(e) => ref
diff --git a/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart b/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart
index b6dcd7bded..1be9783995 100644
--- a/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart
+++ b/lib/pages/address_book_views/subviews/new_contact_address_entry_form.dart
@@ -71,6 +71,7 @@ class _NewContactAddressEntryFormState
// .state)
// .state = false;
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
// Future.delayed(
// const Duration(seconds: 2),
@@ -82,7 +83,7 @@ class _NewContactAddressEntryFormState
// );
final paymentData = AddressUtils.parsePaymentUri(
- qrResult.rawContent,
+ qrResult.rawContent!,
logging: Logging.instance,
);
@@ -93,18 +94,19 @@ class _NewContactAddressEntryFormState
addressLabelController.text =
paymentData.label ?? addressLabelController.text;
- ref.read(addressEntryDataProvider(widget.id)).addressLabel =
- addressLabelController.text.isEmpty
- ? null
- : addressLabelController.text;
+ ref
+ .read(addressEntryDataProvider(widget.id))
+ .addressLabel = addressLabelController.text.isEmpty
+ ? null
+ : addressLabelController.text;
// now check for non standard encoded basic address
} else if (ref.read(addressEntryDataProvider(widget.id)).coin != null) {
if (ref
.read(addressEntryDataProvider(widget.id))
.coin!
- .validateAddress(qrResult.rawContent)) {
- addressController.text = qrResult.rawContent;
+ .validateAddress(qrResult.rawContent!)) {
+ addressController.text = qrResult.rawContent!;
ref.read(addressEntryDataProvider(widget.id)).address =
qrResult.rawContent;
}
@@ -140,13 +142,10 @@ class _NewContactAddressEntryFormState
@override
void initState() {
- addressLabelController =
- TextEditingController()
- ..text =
- ref.read(addressEntryDataProvider(widget.id)).addressLabel ?? "";
- addressController =
- TextEditingController()
- ..text = ref.read(addressEntryDataProvider(widget.id)).address ?? "";
+ addressLabelController = TextEditingController()
+ ..text = ref.read(addressEntryDataProvider(widget.id)).addressLabel ?? "";
+ addressController = TextEditingController()
+ ..text = ref.read(addressEntryDataProvider(widget.id)).address ?? "";
addressLabelFocusNode = FocusNode();
addressFocusNode = FocusNode();
coins = [...AppConfig.coins];
@@ -177,15 +176,15 @@ class _NewContactAddressEntryFormState
coins = [...AppConfig.coins];
coins.removeWhere((e) => e is Firo && e.network.isTestNet);
- final showTestNet =
- ref.read(prefsChangeNotifierProvider).showTestNetCoins;
+ final showTestNet = ref
+ .read(prefsChangeNotifierProvider)
+ .showTestNetCoins;
if (showTestNet) {
coins = coins.toList();
} else {
- coins =
- coins
- .where((e) => e.network != CryptoCurrencyNetwork.test)
- .toList();
+ coins = coins
+ .where((e) => e.network != CryptoCurrencyNetwork.test)
+ .toList();
}
}
@@ -202,10 +201,9 @@ class _NewContactAddressEntryFormState
offset: const Offset(0, -10),
elevation: 0,
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
@@ -249,14 +247,14 @@ class _NewContactAddressEntryFormState
const SizedBox(width: 12),
Text(
coin.prettyName,
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(
context,
).extension()!.textDark,
- ),
+ ),
),
],
),
@@ -279,8 +277,9 @@ class _NewContactAddressEntryFormState
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: RawMaterialButton(
- splashColor:
- Theme.of(context).extension()!.highlight,
+ splashColor: Theme.of(
+ context,
+ ).extension()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
@@ -308,48 +307,47 @@ class _NewContactAddressEntryFormState
) ==
null
? Text(
- "Select cryptocurrency",
- style: STextStyles.fieldLabel(context),
- )
+ "Select cryptocurrency",
+ style: STextStyles.fieldLabel(context),
+ )
: Row(
- children: [
- SvgPicture.file(
- File(
- ref.watch(
- coinIconProvider(
- ref.watch(
+ children: [
+ SvgPicture.file(
+ File(
+ ref.watch(
+ coinIconProvider(
+ ref.watch(
+ addressEntryDataProvider(
+ widget.id,
+ ).select((value) => value.coin),
+ )!,
+ ),
+ ),
+ ),
+ height: 20,
+ width: 20,
+ ),
+ const SizedBox(width: 12),
+ Text(
+ ref
+ .watch(
addressEntryDataProvider(
widget.id,
).select((value) => value.coin),
- )!,
- ),
- ),
+ )!
+ .prettyName,
+ style: STextStyles.itemSubtitle12(context),
),
- height: 20,
- width: 20,
- ),
- const SizedBox(width: 12),
- Text(
- ref
- .watch(
- addressEntryDataProvider(
- widget.id,
- ).select((value) => value.coin),
- )!
- .prettyName,
- style: STextStyles.itemSubtitle12(context),
- ),
- ],
- ),
+ ],
+ ),
if (!isDesktop)
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
- color:
- Theme.of(
- context,
- ).extension()!.textSubtitle2,
+ color: Theme.of(
+ context,
+ ).extension()!.textSubtitle2,
),
],
),
@@ -369,33 +367,35 @@ class _NewContactAddressEntryFormState
focusNode: addressLabelFocusNode,
controller: addressLabelController,
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Enter address label",
- addressLabelFocusNode,
- context,
- ).copyWith(
- labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
- suffixIcon:
- addressLabelController.text.isNotEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter address label",
+ addressLabelFocusNode,
+ context,
+ ).copyWith(
+ labelStyle: isDesktop
+ ? STextStyles.fieldLabel(context)
+ : null,
+ suffixIcon: addressLabelController.text.isNotEmpty
? Padding(
- padding: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- children: [
- TextFieldIconButton(
- child: const XIcon(),
- onTap: () async {
- setState(() {
- addressLabelController.text = "";
- });
- },
- ),
- ],
+ padding: const EdgeInsets.only(right: 0),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ TextFieldIconButton(
+ child: const XIcon(),
+ onTap: () async {
+ setState(() {
+ addressLabelController.text = "";
+ });
+ },
+ ),
+ ],
+ ),
),
- ),
- )
+ )
: null,
- ),
+ ),
onChanged: (newValue) {
ref.read(addressEntryDataProvider(widget.id)).addressLabel =
newValue;
@@ -413,76 +413,87 @@ class _NewContactAddressEntryFormState
focusNode: addressFocusNode,
controller: addressController,
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Paste address",
- addressFocusNode,
- context,
- ).copyWith(
- labelStyle: isDesktop ? STextStyles.fieldLabel(context) : null,
- suffixIcon: UnconstrainedBox(
- child: Row(
- children: [
- if (ref.watch(
- addressEntryDataProvider(
- widget.id,
- ).select((value) => value.address),
- ) !=
- null)
- TextFieldIconButton(
- key: const Key("addAddressBookClearAddressButtonKey"),
- onTap: () async {
- addressController.text = "";
- ref
- .read(addressEntryDataProvider(widget.id))
- .address = null;
- },
- child: const XIcon(),
- ),
- if (ref.watch(
- addressEntryDataProvider(
- widget.id,
- ).select((value) => value.address),
- ) ==
- null)
- TextFieldIconButton(
- key: const Key("addAddressPasteAddressButtonKey"),
- onTap: () async {
- final ClipboardData? data = await widget.clipboard
- .getData(Clipboard.kTextPlain);
-
- if (data?.text != null && data!.text!.isNotEmpty) {
- String content = data.text!.trim();
- if (content.contains("\n")) {
- content = content.substring(
- 0,
- content.indexOf("\n"),
- );
- }
- addressController.text = content;
- ref
- .read(addressEntryDataProvider(widget.id))
- .address = content.isEmpty ? null : content;
- }
- },
- child: const ClipboardIcon(),
- ),
- if (!Util.isDesktop &&
- ref.watch(
+ decoration:
+ standardInputDecoration(
+ "Paste address",
+ addressFocusNode,
+ context,
+ ).copyWith(
+ labelStyle: isDesktop
+ ? STextStyles.fieldLabel(context)
+ : null,
+ suffixIcon: UnconstrainedBox(
+ child: Row(
+ children: [
+ if (ref.watch(
+ addressEntryDataProvider(
+ widget.id,
+ ).select((value) => value.address),
+ ) !=
+ null)
+ TextFieldIconButton(
+ key: const Key(
+ "addAddressBookClearAddressButtonKey",
+ ),
+ onTap: () async {
+ addressController.text = "";
+ ref
+ .read(addressEntryDataProvider(widget.id))
+ .address =
+ null;
+ },
+ child: const XIcon(),
+ ),
+ if (ref.watch(
addressEntryDataProvider(
widget.id,
).select((value) => value.address),
) ==
null)
- TextFieldIconButton(
- key: const Key("addAddressBookEntryScanQrButtonKey"),
- onTap: _onQrTapped,
- child: const QrCodeIcon(),
- ),
- const SizedBox(width: 8),
- ],
+ TextFieldIconButton(
+ key: const Key("addAddressPasteAddressButtonKey"),
+ onTap: () async {
+ final ClipboardData? data = await widget.clipboard
+ .getData(Clipboard.kTextPlain);
+
+ if (data?.text != null &&
+ data!.text!.isNotEmpty) {
+ String content = data.text!.trim();
+ if (content.contains("\n")) {
+ content = content.substring(
+ 0,
+ content.indexOf("\n"),
+ );
+ }
+ addressController.text = content;
+ ref
+ .read(addressEntryDataProvider(widget.id))
+ .address = content.isEmpty
+ ? null
+ : content;
+ }
+ },
+ child: const ClipboardIcon(),
+ ),
+ if (!Util.isDesktop &&
+ ref.watch(
+ addressEntryDataProvider(
+ widget.id,
+ ).select((value) => value.address),
+ ) ==
+ null)
+ TextFieldIconButton(
+ key: const Key(
+ "addAddressBookEntryScanQrButtonKey",
+ ),
+ onTap: _onQrTapped,
+ child: const QrCodeIcon(),
+ ),
+ const SizedBox(width: 8),
+ ],
+ ),
+ ),
),
- ),
- ),
key: const Key("addAddressBookEntryViewAddressField"),
readOnly: false,
autocorrect: false,
@@ -517,8 +528,9 @@ class _NewContactAddressEntryFormState
"Invalid address",
textAlign: TextAlign.left,
style: STextStyles.label(context).copyWith(
- color:
- Theme.of(context).extension()!.textError,
+ color: Theme.of(
+ context,
+ ).extension()!.textError,
),
),
],
diff --git a/lib/pages/buy_view/buy_form.dart b/lib/pages/buy_view/buy_form.dart
index 8f8a0c9b82..47911044de 100644
--- a/lib/pages/buy_view/buy_form.dart
+++ b/lib/pages/buy_view/buy_form.dart
@@ -163,14 +163,13 @@ class _BuyFormState extends ConsumerState {
unawaited(
showDialog(
context: context,
- builder:
- (context) => WillPopScope(
- child: const CustomLoadingOverlay(
- message: "Loading currency data",
- eventBus: null,
- ),
- onWillPop: () async => shouldPop,
- ),
+ builder: (context) => WillPopScope(
+ child: const CustomLoadingOverlay(
+ message: "Loading currency data",
+ eventBus: null,
+ ),
+ onWillPop: () async => shouldPop,
+ ),
),
);
await _loadSimplexCryptos();
@@ -204,62 +203,60 @@ class _BuyFormState extends ConsumerState {
_fiatFocusNode.unfocus();
_cryptoFocusNode.unfocus();
- final result =
- isDesktop
- ? await showDialog(
- context: context,
- builder: (context) {
- return DesktopDialog(
- maxHeight: 700,
- maxWidth: 580,
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "Choose a crypto to buy",
- style: STextStyles.desktopH3(context),
- ),
- ),
- const DesktopDialogCloseButton(),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(
- left: 32,
- right: 32,
- bottom: 32,
+ final result = isDesktop
+ ? await showDialog(
+ context: context,
+ builder: (context) {
+ return DesktopDialog(
+ maxHeight: 700,
+ maxWidth: 580,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "Choose a crypto to buy",
+ style: STextStyles.desktopH3(context),
),
- child: Row(
- children: [
- Expanded(
- child: RoundedWhiteContainer(
- padding: const EdgeInsets.all(16),
- borderColor:
- Theme.of(
- context,
- ).extension()!.background,
- child: CryptoSelectionView(coins: coins),
- ),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: RoundedWhiteContainer(
+ padding: const EdgeInsets.all(16),
+ borderColor: Theme.of(
+ context,
+ ).extension()!.background,
+ child: CryptoSelectionView(coins: coins),
),
- ],
- ),
+ ),
+ ],
),
),
- ],
- ),
- );
- },
- )
- : await Navigator.of(context).push(
- MaterialPageRoute(
- builder: (_) => CryptoSelectionView(coins: coins),
- ),
- );
+ ),
+ ],
+ ),
+ );
+ },
+ )
+ : await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (_) => CryptoSelectionView(coins: coins),
+ ),
+ );
if (mounted && result is Crypto) {
onSelected(result);
@@ -272,14 +269,13 @@ class _BuyFormState extends ConsumerState {
unawaited(
showDialog(
context: context,
- builder:
- (context) => WillPopScope(
- child: const CustomLoadingOverlay(
- message: "Loading currency data",
- eventBus: null,
- ),
- onWillPop: () async => shouldPop,
- ),
+ builder: (context) => WillPopScope(
+ child: const CustomLoadingOverlay(
+ message: "Loading currency data",
+ eventBus: null,
+ ),
+ onWillPop: () async => shouldPop,
+ ),
),
);
await _loadSimplexFiats();
@@ -333,62 +329,60 @@ class _BuyFormState extends ConsumerState {
_fiatFocusNode.unfocus();
_cryptoFocusNode.unfocus();
- final result =
- isDesktop
- ? await showDialog(
- context: context,
- builder: (context) {
- return DesktopDialog(
- maxHeight: 700,
- maxWidth: 580,
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "Choose a fiat with which to pay",
- style: STextStyles.desktopH3(context),
- ),
- ),
- const DesktopDialogCloseButton(),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(
- left: 32,
- right: 32,
- bottom: 32,
+ final result = isDesktop
+ ? await showDialog(
+ context: context,
+ builder: (context) {
+ return DesktopDialog(
+ maxHeight: 700,
+ maxWidth: 580,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "Choose a fiat with which to pay",
+ style: STextStyles.desktopH3(context),
),
- child: Row(
- children: [
- Expanded(
- child: RoundedWhiteContainer(
- padding: const EdgeInsets.all(16),
- borderColor:
- Theme.of(
- context,
- ).extension()!.background,
- child: FiatSelectionView(fiats: fiats),
- ),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: RoundedWhiteContainer(
+ padding: const EdgeInsets.all(16),
+ borderColor: Theme.of(
+ context,
+ ).extension()!.background,
+ child: FiatSelectionView(fiats: fiats),
),
- ],
- ),
+ ),
+ ],
),
),
- ],
- ),
- );
- },
- )
- : await Navigator.of(context).push(
- MaterialPageRoute(
- builder: (_) => FiatSelectionView(fiats: fiats),
- ),
- );
+ ),
+ ],
+ ),
+ );
+ },
+ )
+ : await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (_) => FiatSelectionView(fiats: fiats),
+ ),
+ );
if (mounted && result is Fiat) {
onSelected(result);
@@ -406,28 +400,25 @@ class _BuyFormState extends ConsumerState {
unawaited(
showDialog(
context: context,
- builder:
- (context) => WillPopScope(
- child: const CustomLoadingOverlay(
- message: "Loading quote data",
- eventBus: null,
- ),
- onWillPop: () async => shouldPop,
- ),
+ builder: (context) => WillPopScope(
+ child: const CustomLoadingOverlay(
+ message: "Loading quote data",
+ eventBus: null,
+ ),
+ onWillPop: () async => shouldPop,
+ ),
),
);
quote = SimplexQuote(
crypto: selectedCrypto!,
fiat: selectedFiat!,
- youPayFiatPrice:
- buyWithFiat
- ? Decimal.parse(_buyAmountController.text)
- : Decimal.parse("100"), // dummy value
- youReceiveCryptoAmount:
- buyWithFiat
- ? Decimal.parse("0.000420282") // dummy value
- : Decimal.parse(_buyAmountController.text), // Ternary for this
+ youPayFiatPrice: buyWithFiat
+ ? Decimal.parse(_buyAmountController.text)
+ : Decimal.parse("100"), // dummy value
+ youReceiveCryptoAmount: buyWithFiat
+ ? Decimal.parse("0.000420282") // dummy value
+ : Decimal.parse(_buyAmountController.text), // Ternary for this
id: "id", // anything; we get an ID back
receivingAddress: _receiveAddressController.text,
buyWithFiat: buyWithFiat,
@@ -500,10 +491,9 @@ class _BuyFormState extends ConsumerState {
child: Text(
"Ok",
style: STextStyles.button(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension()!.accentColorDark,
),
),
onPressed: () {
@@ -587,10 +577,9 @@ class _BuyFormState extends ConsumerState {
child: Text(
"Ok",
style: STextStyles.button(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension()!.accentColorDark,
),
),
onPressed: () {
@@ -628,62 +617,60 @@ class _BuyFormState extends ConsumerState {
_fiatFocusNode.unfocus();
_cryptoFocusNode.unfocus();
- final result =
- isDesktop
- ? await showDialog(
- context: context,
- builder: (context) {
- return DesktopDialog(
- maxHeight: 700,
- maxWidth: 580,
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "Preview quote",
- style: STextStyles.desktopH3(context),
- ),
- ),
- const DesktopDialogCloseButton(),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.only(
- left: 32,
- right: 32,
- bottom: 32,
+ final result = isDesktop
+ ? await showDialog(
+ context: context,
+ builder: (context) {
+ return DesktopDialog(
+ maxHeight: 700,
+ maxWidth: 580,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "Preview quote",
+ style: STextStyles.desktopH3(context),
),
- child: Row(
- children: [
- Expanded(
- child: RoundedWhiteContainer(
- padding: const EdgeInsets.all(16),
- borderColor:
- Theme.of(
- context,
- ).extension()!.background,
- child: BuyQuotePreviewView(quote: quote),
- ),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.only(
+ left: 32,
+ right: 32,
+ bottom: 32,
+ ),
+ child: Row(
+ children: [
+ Expanded(
+ child: RoundedWhiteContainer(
+ padding: const EdgeInsets.all(16),
+ borderColor: Theme.of(
+ context,
+ ).extension()!.background,
+ child: BuyQuotePreviewView(quote: quote),
),
- ],
- ),
+ ),
+ ],
),
),
- ],
- ),
- );
- },
- )
- : await Navigator.of(context).push(
- MaterialPageRoute(
- builder: (_) => BuyQuotePreviewView(quote: quote),
- ),
- );
+ ),
+ ],
+ ),
+ );
+ },
+ )
+ : await Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (_) => BuyQuotePreviewView(quote: quote),
+ ),
+ );
if (mounted && result is SimplexQuote) {
onSelected(result);
@@ -698,11 +685,12 @@ class _BuyFormState extends ConsumerState {
}
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
Logging.instance.d("qrResult content: ${qrResult.rawContent}");
final paymentData = AddressUtils.parsePaymentUri(
- qrResult.rawContent,
+ qrResult.rawContent!,
logging: Logging.instance,
);
@@ -816,18 +804,14 @@ class _BuyFormState extends ConsumerState {
builder: (child) => SizedBox(width: 458, child: child),
child: ConditionalParent(
condition: !isDesktop,
- builder:
- (child) => LayoutBuilder(
- builder:
- (context, constraints) => SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight,
- ),
- child: IntrinsicHeight(child: child),
- ),
- ),
+ builder: (child) => LayoutBuilder(
+ builder: (context, constraints) => SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(minHeight: constraints.maxHeight),
+ child: IntrinsicHeight(child: child),
),
+ ),
+ ),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -852,15 +836,14 @@ class _BuyFormState extends ConsumerState {
vertical: 6,
horizontal: 2,
),
- color:
- _hovering1
- ? Theme.of(context)
- .extension()!
- .currencyListItemBG
- .withOpacity(_hovering1 ? 0.3 : 0)
- : Theme.of(
- context,
- ).extension()!.textFieldDefaultBG,
+ color: _hovering1
+ ? Theme.of(context)
+ .extension()!
+ .currencyListItemBG
+ .withOpacity(_hovering1 ? 0.3 : 0)
+ : Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
@@ -878,10 +861,9 @@ class _BuyFormState extends ConsumerState {
),
SvgPicture.asset(
Assets.svg.chevronDown,
- color:
- Theme.of(context)
- .extension()!
- .buttonTextSecondaryDisabled,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextSecondaryDisabled,
width: 10,
height: 5,
),
@@ -899,8 +881,9 @@ class _BuyFormState extends ConsumerState {
Text(
"I want to pay with",
style: STextStyles.itemSubtitle(context).copyWith(
- color:
- Theme.of(context).extension()!.textDark3,
+ color: Theme.of(
+ context,
+ ).extension()!.textDark3,
),
),
],
@@ -919,15 +902,14 @@ class _BuyFormState extends ConsumerState {
vertical: 3,
horizontal: 2,
),
- color:
- _hovering2
- ? Theme.of(context)
- .extension()!
- .currencyListItemBG
- .withOpacity(_hovering2 ? 0.3 : 0)
- : Theme.of(
- context,
- ).extension()!.textFieldDefaultBG,
+ color: _hovering2
+ ? Theme.of(context)
+ .extension()!
+ .currencyListItemBG
+ .withOpacity(_hovering2 ? 0.3 : 0)
+ : Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
child: Padding(
padding: const EdgeInsets.only(
left: 12.0,
@@ -943,10 +925,9 @@ class _BuyFormState extends ConsumerState {
horizontal: 6,
),
decoration: BoxDecoration(
- color:
- Theme.of(
- context,
- ).extension()!.currencyListItemBG,
+ color: Theme.of(
+ context,
+ ).extension()!.currencyListItemBG,
borderRadius: BorderRadius.circular(4),
),
child: Text(
@@ -955,10 +936,9 @@ class _BuyFormState extends ConsumerState {
),
textAlign: TextAlign.center,
style: STextStyles.smallMed12(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension()!.accentColorDark,
),
),
),
@@ -976,10 +956,9 @@ class _BuyFormState extends ConsumerState {
),
SvgPicture.asset(
Assets.svg.chevronDown,
- color:
- Theme.of(context)
- .extension()!
- .buttonTextSecondaryDisabled,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextSecondaryDisabled,
width: 10,
height: 5,
),
@@ -997,8 +976,9 @@ class _BuyFormState extends ConsumerState {
Text(
buyWithFiat ? "Enter amount" : "Enter crypto amount",
style: STextStyles.itemSubtitle(context).copyWith(
- color:
- Theme.of(context).extension()!.textDark3,
+ color: Theme.of(
+ context,
+ ).extension()!.textDark3,
),
),
CustomTextButton(
@@ -1026,13 +1006,12 @@ class _BuyFormState extends ConsumerState {
// ? _BuyFormState.minFiat.toStringAsFixed(2) ?? '50.00'
// : _BuyFormState.minCrypto.toStringAsFixed(8),
focusNode: _buyAmountFocusNode,
- keyboardType:
- Util.isDesktop
- ? null
- : const TextInputType.numberWithOptions(
- signed: false,
- decimal: true,
- ),
+ keyboardType: Util.isDesktop
+ ? null
+ : const TextInputType.numberWithOptions(
+ signed: false,
+ decimal: true,
+ ),
textAlign: TextAlign.left,
// inputFormatters: [NumericalRangeFormatter()],
onChanged: (_) {
@@ -1050,10 +1029,9 @@ class _BuyFormState extends ConsumerState {
),
hintText: "0",
hintStyle: STextStyles.desktopTextExtraSmall(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.textFieldDefaultText,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultText,
),
prefixIcon: FittedBox(
fit: BoxFit.scaleDown,
@@ -1064,34 +1042,33 @@ class _BuyFormState extends ConsumerState {
const SizedBox(width: 2),
buyWithFiat
? Container(
- padding: const EdgeInsets.symmetric(
- vertical: 3,
- horizontal: 6,
- ),
- decoration: BoxDecoration(
- color:
- Theme.of(context)
- .extension()!
- .currencyListItemBG,
- borderRadius: BorderRadius.circular(4),
- ),
- child: Text(
- format.simpleCurrencySymbol(
- selectedFiat?.ticker.toUpperCase() ?? "ERR",
+ padding: const EdgeInsets.symmetric(
+ vertical: 3,
+ horizontal: 6,
),
- textAlign: TextAlign.center,
- style: STextStyles.smallMed12(context).copyWith(
- color:
- Theme.of(context)
- .extension()!
- .accentColorDark,
+ decoration: BoxDecoration(
+ color: Theme.of(context)
+ .extension()!
+ .currencyListItemBG,
+ borderRadius: BorderRadius.circular(4),
),
- ),
- )
+ child: Text(
+ format.simpleCurrencySymbol(
+ selectedFiat?.ticker.toUpperCase() ?? "ERR",
+ ),
+ textAlign: TextAlign.center,
+ style: STextStyles.smallMed12(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .accentColorDark,
+ ),
+ ),
+ )
: CoinIconForTicker(
- ticker: selectedCrypto?.ticker ?? "BTC",
- size: 20,
- ),
+ ticker: selectedCrypto?.ticker ?? "BTC",
+ size: 20,
+ ),
SizedBox(
width: buyWithFiat ? 8 : 10,
), // maybe make isDesktop-aware?
@@ -1100,10 +1077,9 @@ class _BuyFormState extends ConsumerState {
? selectedFiat?.ticker ?? "ERR"
: selectedCrypto?.ticker ?? "ERR",
style: STextStyles.smallMed14(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.accentColorDark,
+ color: Theme.of(
+ context,
+ ).extension()!.accentColorDark,
),
),
],
@@ -1118,50 +1094,49 @@ class _BuyFormState extends ConsumerState {
children: [
_buyAmountController.text.isNotEmpty
? TextFieldIconButton(
- key: const Key(
- "buyViewClearAmountFieldButtonKey",
- ),
- onTap: () {
- // if (_BuyFormState.buyWithFiat) {
- // _buyAmountController.text = _BuyFormState
- // .minFiat
- // .toStringAsFixed(2);
- // } else {
- // if (selectedCrypto?.ticker ==
- // _BuyFormState.boundedCryptoTicker) {
- // _buyAmountController.text = _BuyFormState
- // .minCrypto
- // .toStringAsFixed(8);
- // }
- // }
- _buyAmountController.text = "";
- validateAmount();
- },
- child: const XIcon(),
- )
+ key: const Key(
+ "buyViewClearAmountFieldButtonKey",
+ ),
+ onTap: () {
+ // if (_BuyFormState.buyWithFiat) {
+ // _buyAmountController.text = _BuyFormState
+ // .minFiat
+ // .toStringAsFixed(2);
+ // } else {
+ // if (selectedCrypto?.ticker ==
+ // _BuyFormState.boundedCryptoTicker) {
+ // _buyAmountController.text = _BuyFormState
+ // .minCrypto
+ // .toStringAsFixed(8);
+ // }
+ // }
+ _buyAmountController.text = "";
+ validateAmount();
+ },
+ child: const XIcon(),
+ )
: TextFieldIconButton(
- key: const Key(
- "buyViewPasteAddressFieldButtonKey",
- ),
- onTap: () async {
- final ClipboardData? data = await clipboard
- .getData(Clipboard.kTextPlain);
+ key: const Key(
+ "buyViewPasteAddressFieldButtonKey",
+ ),
+ onTap: () async {
+ final ClipboardData? data = await clipboard
+ .getData(Clipboard.kTextPlain);
- final amountString = Decimal.tryParse(
- data?.text ?? "",
- );
- if (amountString != null) {
- _buyAmountController.text =
- amountString.toString();
+ final amountString = Decimal.tryParse(
+ data?.text ?? "",
+ );
+ if (amountString != null) {
+ _buyAmountController.text = amountString
+ .toString();
- validateAmount();
- }
- },
- child:
- _buyAmountController.text.isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
+ validateAmount();
+ }
+ },
+ child: _buyAmountController.text.isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
],
),
),
@@ -1182,8 +1157,9 @@ class _BuyFormState extends ConsumerState {
Text(
"Enter receiving address",
style: STextStyles.itemSubtitle(context).copyWith(
- color:
- Theme.of(context).extension()!.textDark3,
+ color: Theme.of(
+ context,
+ ).extension()!.textDark3,
),
),
if (AppConfig.isStackCoin(selectedCrypto?.ticker))
@@ -1209,8 +1185,8 @@ class _BuyFormState extends ConsumerState {
// model.recipientAddress =
// await manager.currentReceivingAddress;
- final address =
- await wallet.getCurrentReceivingAddress();
+ final address = await wallet
+ .getCurrentReceivingAddress();
if (address!.type == AddressType.p2tr &&
wallet is Bip39HDWallet) {
@@ -1289,85 +1265,87 @@ class _BuyFormState extends ConsumerState {
},
focusNode: _receiveAddressFocusNode,
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Enter ${selectedCrypto?.ticker} address",
- _receiveAddressFocusNode,
- context,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 13,
- top: 6,
- bottom: 8,
- right: 5,
- ),
- suffixIcon: Padding(
- padding:
- _receiveAddressController.text.isEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter ${selectedCrypto?.ticker} address",
+ _receiveAddressFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 13,
+ top: 6,
+ bottom: 8,
+ right: 5,
+ ),
+ suffixIcon: Padding(
+ padding: _receiveAddressController.text.isEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- _addressToggleFlag
- ? TextFieldIconButton(
- key: const Key(
- "buyViewClearAddressFieldButtonKey",
- ),
- onTap: () {
- _receiveAddressController.text = "";
- _address = "";
- setState(() {
- _addressToggleFlag = false;
- });
- },
- child: const XIcon(),
- )
- : TextFieldIconButton(
- key: const Key(
- "buyViewPasteAddressFieldButtonKey",
- ),
- onTap: () async {
- final ClipboardData? data = await clipboard
- .getData(Clipboard.kTextPlain);
- if (data?.text != null &&
- data!.text!.isNotEmpty) {
- String content = data.text!.trim();
- if (content.contains("\n")) {
- content = content.substring(
- 0,
- content.indexOf("\n"),
- );
- }
-
- _receiveAddressController.text = content;
- _address = content;
-
- setState(() {
- _addressToggleFlag =
- _receiveAddressController
- .text
- .isNotEmpty;
- });
- }
- },
- child:
- _receiveAddressController.text.isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
- if (_receiveAddressController.text.isEmpty &&
- AppConfig.isStackCoin(selectedCrypto?.ticker) &&
- isDesktop)
- TextFieldIconButton(
- key: const Key("buyViewAddressBookButtonKey"),
- onTap: () async {
- final entry = await showDialog<
- ContactAddressEntry?
- >(
- context: context,
- builder:
- (context) => DesktopDialog(
+ child: UnconstrainedBox(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ _addressToggleFlag
+ ? TextFieldIconButton(
+ key: const Key(
+ "buyViewClearAddressFieldButtonKey",
+ ),
+ onTap: () {
+ _receiveAddressController.text = "";
+ _address = "";
+ setState(() {
+ _addressToggleFlag = false;
+ });
+ },
+ child: const XIcon(),
+ )
+ : TextFieldIconButton(
+ key: const Key(
+ "buyViewPasteAddressFieldButtonKey",
+ ),
+ onTap: () async {
+ final ClipboardData? data =
+ await clipboard.getData(
+ Clipboard.kTextPlain,
+ );
+ if (data?.text != null &&
+ data!.text!.isNotEmpty) {
+ String content = data.text!.trim();
+ if (content.contains("\n")) {
+ content = content.substring(
+ 0,
+ content.indexOf("\n"),
+ );
+ }
+
+ _receiveAddressController.text =
+ content;
+ _address = content;
+
+ setState(() {
+ _addressToggleFlag =
+ _receiveAddressController
+ .text
+ .isNotEmpty;
+ });
+ }
+ },
+ child:
+ _receiveAddressController.text.isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
+ if (_receiveAddressController.text.isEmpty &&
+ AppConfig.isStackCoin(
+ selectedCrypto?.ticker,
+ ) &&
+ isDesktop)
+ TextFieldIconButton(
+ key: const Key("buyViewAddressBookButtonKey"),
+ onTap: () async {
+ final entry = await showDialog(
+ context: context,
+ builder: (context) => DesktopDialog(
maxWidth: 696,
maxHeight: 600,
child: Column(
@@ -1410,45 +1388,47 @@ class _BuyFormState extends ConsumerState {
],
),
),
- );
+ );
- if (entry != null) {
- _receiveAddressController.text =
- entry.address;
- _address = entry.address;
+ if (entry != null) {
+ _receiveAddressController.text =
+ entry.address;
+ _address = entry.address;
- setState(() {
- _addressToggleFlag = true;
- });
- }
- },
- child: const AddressBookIcon(),
- ),
- if (_receiveAddressController.text.isEmpty &&
- AppConfig.isStackCoin(selectedCrypto?.ticker) &&
- !isDesktop)
- TextFieldIconButton(
- key: const Key("buyViewAddressBookButtonKey"),
- onTap: () {
- Navigator.of(
- context,
- rootNavigator: isDesktop,
- ).pushNamed(AddressBookView.routeName);
- },
- child: const AddressBookIcon(),
- ),
- if (_receiveAddressController.text.isEmpty &&
- !isDesktop)
- TextFieldIconButton(
- key: const Key("buyViewScanQrButtonKey"),
- onTap: _onQrTapped,
- child: const QrCodeIcon(),
- ),
- ],
+ setState(() {
+ _addressToggleFlag = true;
+ });
+ }
+ },
+ child: const AddressBookIcon(),
+ ),
+ if (_receiveAddressController.text.isEmpty &&
+ AppConfig.isStackCoin(
+ selectedCrypto?.ticker,
+ ) &&
+ !isDesktop)
+ TextFieldIconButton(
+ key: const Key("buyViewAddressBookButtonKey"),
+ onTap: () {
+ Navigator.of(
+ context,
+ rootNavigator: isDesktop,
+ ).pushNamed(AddressBookView.routeName);
+ },
+ child: const AddressBookIcon(),
+ ),
+ if (_receiveAddressController.text.isEmpty &&
+ !isDesktop)
+ TextFieldIconButton(
+ key: const Key("buyViewScanQrButtonKey"),
+ onTap: _onQrTapped,
+ child: const QrCodeIcon(),
+ ),
+ ],
+ ),
+ ),
),
),
- ),
- ),
),
),
SizedBox(height: isDesktop ? 10 : 4),
diff --git a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart
index a731bf9dfc..3bead4106e 100644
--- a/lib/pages/exchange_view/exchange_step_views/step_2_view.dart
+++ b/lib/pages/exchange_view/exchange_step_views/step_2_view.dart
@@ -70,9 +70,10 @@ class _Step2ViewState extends ConsumerState {
void _onRefundQrTapped() async {
try {
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
final paymentData = AddressUtils.parsePaymentUri(
- qrResult.rawContent,
+ qrResult.rawContent!,
logging: Logging.instance,
);
@@ -87,7 +88,7 @@ class _Step2ViewState extends ConsumerState {
_refundController.text.isNotEmpty;
});
} else {
- _refundController.text = qrResult.rawContent;
+ _refundController.text = qrResult.rawContent!;
model.refundAddress = _refundController.text;
setState(() {
@@ -123,9 +124,10 @@ class _Step2ViewState extends ConsumerState {
void _onToQrTapped() async {
try {
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
final paymentData = AddressUtils.parsePaymentUri(
- qrResult.rawContent,
+ qrResult.rawContent!,
logging: Logging.instance,
);
@@ -141,7 +143,7 @@ class _Step2ViewState extends ConsumerState {
!ref.read(efExchangeProvider).supportsRefundAddress);
});
} else {
- _toController.text = qrResult.rawContent;
+ _toController.text = qrResult.rawContent!;
model.recipientAddress = _toController.text;
setState(() {
@@ -373,156 +375,168 @@ class _Step2ViewState extends ConsumerState {
!supportsRefund);
});
},
- decoration: standardInputDecoration(
- "Enter the ${model.receiveTicker.toUpperCase()} payout address",
- _toFocusNode,
- context,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 16,
- top: 6,
- bottom: 8,
- right: 5,
- ),
- suffixIcon: Padding(
- padding:
- _toController.text.isEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter the ${model.receiveTicker.toUpperCase()} payout address",
+ _toFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ top: 6,
+ bottom: 8,
+ right: 5,
+ ),
+ suffixIcon: Padding(
+ padding: _toController.text.isEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceAround,
- children: [
- _toController.text.isNotEmpty
- ? TextFieldIconButton(
- key: const Key(
- "sendViewClearAddressFieldButtonKey",
- ),
- onTap: () {
- _toController.text = "";
- model.recipientAddress =
- _toController.text;
-
- setState(() {
- enableNext =
- _toController
- .text
- .isNotEmpty &&
- (_refundController
- .text
- .isNotEmpty ||
- !supportsRefund);
- });
- },
- child: const XIcon(),
- )
- : TextFieldIconButton(
- key: const Key(
- "sendViewPasteAddressFieldButtonKey",
- ),
- onTap: () async {
- final ClipboardData? data =
- await clipboard.getData(
- Clipboard.kTextPlain,
- );
- if (data?.text != null &&
- data!.text!.isNotEmpty) {
- final content =
- data.text!.trim();
-
- _toController.text =
- content;
- model.recipientAddress =
- _toController.text;
-
- setState(() {
- enableNext =
- _toController
- .text
- .isNotEmpty &&
- (_refundController
+ child: UnconstrainedBox(
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceAround,
+ children: [
+ _toController.text.isNotEmpty
+ ? TextFieldIconButton(
+ key: const Key(
+ "sendViewClearAddressFieldButtonKey",
+ ),
+ onTap: () {
+ _toController.text = "";
+ model.recipientAddress =
+ _toController.text;
+
+ setState(() {
+ enableNext =
+ _toController
.text
- .isNotEmpty ||
- !supportsRefund);
- });
- }
- },
- child:
- _toController.text.isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
- if (_toController.text.isEmpty)
- TextFieldIconButton(
- key: const Key(
- "sendViewAddressBookButtonKey",
- ),
- onTap: () {
- ref
- .read(
- exchangeFlowIsActiveStateProvider
- .state,
+ .isNotEmpty &&
+ (_refundController
+ .text
+ .isNotEmpty ||
+ !supportsRefund);
+ });
+ },
+ child: const XIcon(),
)
- .state = true;
- Navigator.of(
- context,
- ).pushNamed(AddressBookView.routeName).then((
- _,
- ) {
- ref
- .read(
- exchangeFlowIsActiveStateProvider
- .state,
- )
- .state = false;
+ : TextFieldIconButton(
+ key: const Key(
+ "sendViewPasteAddressFieldButtonKey",
+ ),
+ onTap: () async {
+ final ClipboardData?
+ data = await clipboard
+ .getData(
+ Clipboard
+ .kTextPlain,
+ );
+ if (data?.text !=
+ null &&
+ data!
+ .text!
+ .isNotEmpty) {
+ final content = data
+ .text!
+ .trim();
+
+ _toController.text =
+ content;
+ model.recipientAddress =
+ _toController
+ .text;
- final address =
+ setState(() {
+ enableNext =
+ _toController
+ .text
+ .isNotEmpty &&
+ (_refundController
+ .text
+ .isNotEmpty ||
+ !supportsRefund);
+ });
+ }
+ },
+ child:
+ _toController
+ .text
+ .isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
+ if (_toController.text.isEmpty)
+ TextFieldIconButton(
+ key: const Key(
+ "sendViewAddressBookButtonKey",
+ ),
+ onTap: () {
+ ref
+ .read(
+ exchangeFlowIsActiveStateProvider
+ .state,
+ )
+ .state =
+ true;
+ Navigator.of(
+ context,
+ ).pushNamed(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(
- exchangeFromAddressBookAddressStateProvider
- .state,
- )
- .state = "";
- }
- setState(() {
- enableNext =
- _toController
- .text
- .isNotEmpty &&
- (_refundController
+ if (address.isNotEmpty) {
+ _toController.text =
+ address;
+ model.recipientAddress =
+ _toController.text;
+ ref
+ .read(
+ exchangeFromAddressBookAddressStateProvider
+ .state,
+ )
+ .state =
+ "";
+ }
+ setState(() {
+ enableNext =
+ _toController
.text
- .isNotEmpty ||
- !supportsRefund);
- });
- });
- },
- child: const AddressBookIcon(),
- ),
- if (_toController.text.isEmpty)
- TextFieldIconButton(
- key: const Key(
- "sendViewScanQrButtonKey",
- ),
- onTap: _onToQrTapped,
- child: const QrCodeIcon(),
- ),
- ],
+ .isNotEmpty &&
+ (_refundController
+ .text
+ .isNotEmpty ||
+ !supportsRefund);
+ });
+ });
+ },
+ child:
+ const AddressBookIcon(),
+ ),
+ if (_toController.text.isEmpty)
+ TextFieldIconButton(
+ key: const Key(
+ "sendViewScanQrButtonKey",
+ ),
+ onTap: _onToQrTapped,
+ child: const QrCodeIcon(),
+ ),
+ ],
+ ),
+ ),
),
),
- ),
- ),
),
),
const SizedBox(height: 6),
@@ -628,154 +642,172 @@ class _Step2ViewState extends ConsumerState {
_refundController.text.isNotEmpty;
});
},
- decoration: standardInputDecoration(
- "Enter ${model.sendTicker.toUpperCase()} refund address",
- _refundFocusNode,
- context,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 16,
- top: 6,
- bottom: 8,
- right: 5,
- ),
- suffixIcon: Padding(
- padding:
- _refundController.text.isEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter ${model.sendTicker.toUpperCase()} refund address",
+ _refundFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ top: 6,
+ bottom: 8,
+ right: 5,
+ ),
+ suffixIcon: Padding(
+ padding:
+ _refundController.text.isEmpty
? const EdgeInsets.only(right: 16)
: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceAround,
- children: [
- _refundController.text.isNotEmpty
- ? TextFieldIconButton(
- key: const Key(
- "sendViewClearAddressFieldButtonKey",
- ),
- onTap: () {
- _refundController.text = "";
- model.refundAddress =
- _refundController.text;
-
- setState(() {
- enableNext =
- _toController
- .text
- .isNotEmpty &&
- _refundController
- .text
- .isNotEmpty;
- });
- },
- child: const XIcon(),
- )
- : TextFieldIconButton(
- key: const Key(
- "sendViewPasteAddressFieldButtonKey",
- ),
- onTap: () async {
- final ClipboardData? data =
- await clipboard.getData(
- Clipboard.kTextPlain,
- );
- if (data?.text != null &&
- data!
- .text!
- .isNotEmpty) {
- final content =
- data.text!.trim();
-
- _refundController.text =
- content;
- model.refundAddress =
+ child: UnconstrainedBox(
+ child: Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceAround,
+ children: [
+ _refundController
+ .text
+ .isNotEmpty
+ ? TextFieldIconButton(
+ key: const Key(
+ "sendViewClearAddressFieldButtonKey",
+ ),
+ onTap: () {
_refundController
- .text;
+ .text =
+ "";
+ model.refundAddress =
+ _refundController
+ .text;
+
+ setState(() {
+ enableNext =
+ _toController
+ .text
+ .isNotEmpty &&
+ _refundController
+ .text
+ .isNotEmpty;
+ });
+ },
+ child: const XIcon(),
+ )
+ : TextFieldIconButton(
+ key: const Key(
+ "sendViewPasteAddressFieldButtonKey",
+ ),
+ onTap: () async {
+ final ClipboardData?
+ data = await clipboard
+ .getData(
+ Clipboard
+ .kTextPlain,
+ );
+ if (data?.text !=
+ null &&
+ data!
+ .text!
+ .isNotEmpty) {
+ final content = data
+ .text!
+ .trim();
- setState(() {
- enableNext =
- _toController
- .text
- .isNotEmpty &&
+ _refundController
+ .text =
+ content;
+ model.refundAddress =
+ _refundController
+ .text;
+
+ setState(() {
+ enableNext =
+ _toController
+ .text
+ .isNotEmpty &&
+ _refundController
+ .text
+ .isNotEmpty;
+ });
+ }
+ },
+ child:
_refundController
.text
- .isNotEmpty;
- });
- }
- },
- child:
- _refundController
- .text
- .isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
- if (_refundController.text.isEmpty)
- TextFieldIconButton(
- key: const Key(
- "sendViewAddressBookButtonKey",
- ),
- onTap: () {
- ref
- .read(
- exchangeFlowIsActiveStateProvider
- .state,
- )
- .state = true;
- Navigator.of(context)
- .pushNamed(
- AddressBookView
- .routeName,
- )
- .then((_) {
- ref
- .read(
- exchangeFlowIsActiveStateProvider
- .state,
- )
- .state = false;
- final address =
+ .isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
+ if (_refundController
+ .text
+ .isEmpty)
+ TextFieldIconButton(
+ key: const Key(
+ "sendViewAddressBookButtonKey",
+ ),
+ onTap: () {
+ ref
+ .read(
+ exchangeFlowIsActiveStateProvider
+ .state,
+ )
+ .state =
+ true;
+ Navigator.of(context)
+ .pushNamed(
+ AddressBookView
+ .routeName,
+ )
+ .then((_) {
ref
+ .read(
+ exchangeFlowIsActiveStateProvider
+ .state,
+ )
+ .state =
+ false;
+ final address = ref
.read(
exchangeFromAddressBookAddressStateProvider
.state,
)
.state;
- if (address
- .isNotEmpty) {
- _refundController
- .text = address;
- model.refundAddress =
+ if (address
+ .isNotEmpty) {
_refundController
- .text;
- }
- setState(() {
- enableNext =
- _toController
- .text
- .isNotEmpty &&
- _refundController
- .text
- .isNotEmpty;
- });
- });
- },
- child: const AddressBookIcon(),
- ),
- if (_refundController.text.isEmpty)
- TextFieldIconButton(
- key: const Key(
- "sendViewScanQrButtonKey",
- ),
- onTap: _onRefundQrTapped,
- child: const QrCodeIcon(),
- ),
- ],
+ .text =
+ address;
+ model.refundAddress =
+ _refundController
+ .text;
+ }
+ setState(() {
+ enableNext =
+ _toController
+ .text
+ .isNotEmpty &&
+ _refundController
+ .text
+ .isNotEmpty;
+ });
+ });
+ },
+ child:
+ const AddressBookIcon(),
+ ),
+ if (_refundController
+ .text
+ .isEmpty)
+ TextFieldIconButton(
+ key: const Key(
+ "sendViewScanQrButtonKey",
+ ),
+ onTap: _onRefundQrTapped,
+ child: const QrCodeIcon(),
+ ),
+ ],
+ ),
+ ),
),
),
- ),
- ),
),
),
if (supportsRefund) const SizedBox(height: 6),
@@ -802,14 +834,12 @@ class _Step2ViewState extends ConsumerState {
),
child: Text(
"Back",
- style: STextStyles.button(
- context,
- ).copyWith(
- color:
- Theme.of(context)
+ style: STextStyles.button(context)
+ .copyWith(
+ color: Theme.of(context)
.extension()!
.buttonTextSecondary,
- ),
+ ),
),
),
),
diff --git a/lib/pages/finalize_view/finalize_view.dart b/lib/pages/finalize_view/finalize_view.dart
index e0a3415907..65fb50a1be 100644
--- a/lib/pages/finalize_view/finalize_view.dart
+++ b/lib/pages/finalize_view/finalize_view.dart
@@ -80,8 +80,8 @@ class _FinalizeViewState extends ConsumerState {
if (mounted) {
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
- if (qrResult.rawContent.isNotEmpty && qrResult.rawContent != "null") {
- _slateController.text = qrResult.rawContent;
+ if (qrResult.rawContent != null && qrResult.rawContent!.isNotEmpty) {
+ _slateController.text = qrResult.rawContent!;
setState(() {
_slateToggleFlag = _slateController.text.isNotEmpty;
});
@@ -156,14 +156,12 @@ class _FinalizeViewState extends ConsumerState {
if (ex != null) {
await showDialog(
context: context,
- builder:
- (context) => StackOkDialog(
- desktopPopRootNavigator: Util.isDesktop,
- title: "Slatepack finalize error",
- message:
- ex?.toString() ?? "Unexpected result without exception",
- maxWidth: Util.isDesktop ? 400 : null,
- ),
+ builder: (context) => StackOkDialog(
+ desktopPopRootNavigator: Util.isDesktop,
+ title: "Slatepack finalize error",
+ message: ex?.toString() ?? "Unexpected result without exception",
+ maxWidth: Util.isDesktop ? 400 : null,
+ ),
);
} else {
setState(() {
@@ -201,45 +199,45 @@ class _FinalizeViewState extends ConsumerState {
return ConditionalParent(
condition: !Util.isDesktop,
- builder:
- (child) => Background(
- child: Scaffold(
- backgroundColor:
- Theme.of(context).extension()!.background,
- appBar: AppBar(
- leading: AppBarBackButton(
- onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- title: Text(
- "Finalize slatepack",
- style: STextStyles.navBarTitle(context),
- ),
- ),
- body: SafeArea(
- child: LayoutBuilder(
- builder: (context, constraints) {
- return SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight,
- ),
- child: IntrinsicHeight(
- child: Padding(
- padding: EdgeInsets.symmetric(
- horizontal: Constants.size.standardPadding,
- ),
- child: child,
- ),
+ builder: (child) => Background(
+ child: Scaffold(
+ backgroundColor: Theme.of(
+ context,
+ ).extension()!.background,
+ appBar: AppBar(
+ leading: AppBarBackButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ title: Text(
+ "Finalize slatepack",
+ style: STextStyles.navBarTitle(context),
+ ),
+ ),
+ body: SafeArea(
+ child: LayoutBuilder(
+ builder: (context, constraints) {
+ return SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: constraints.maxHeight,
+ ),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: EdgeInsets.symmetric(
+ horizontal: Constants.size.standardPadding,
),
+ child: child,
),
- );
- },
- ),
- ),
+ ),
+ ),
+ );
+ },
),
),
+ ),
+ ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
@@ -268,61 +266,61 @@ class _FinalizeViewState extends ConsumerState {
},
focusNode: _slateFocusNode,
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Enter Final Slatepack Message",
- _slateFocusNode,
- context,
- desktopMed: true,
- ).copyWith(
- contentPadding: const EdgeInsets.symmetric(
- horizontal: 16,
- vertical: 12, // Adjust vertical padding for better alignment
- ),
- suffixIcon: Padding(
- padding:
- _slateController.text.isEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter Final Slatepack Message",
+ _slateFocusNode,
+ context,
+ desktopMed: true,
+ ).copyWith(
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 16,
+ vertical:
+ 12, // Adjust vertical padding for better alignment
+ ),
+ suffixIcon: Padding(
+ padding: _slateController.text.isEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- _slateToggleFlag
- ? TextFieldIconButton(
- key: const Key(
- "slateFinalizeClearFieldButtonKey",
+ child: UnconstrainedBox(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ _slateToggleFlag
+ ? TextFieldIconButton(
+ key: const Key(
+ "slateFinalizeClearFieldButtonKey",
+ ),
+ onTap: () {
+ _slateController.text = "";
+ setState(() {
+ _slateToggleFlag = false;
+ });
+ },
+ child: const XIcon(),
+ )
+ : TextFieldIconButton(
+ key: const Key(
+ "slateFinalizePasteFieldButtonKey",
+ ),
+ onTap: _pasteSlatepack,
+ child: _slateController.text.isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
+ if (_slateController.text.isEmpty)
+ TextFieldIconButton(
+ semanticsLabel:
+ "Scan QR Button. Opens Camera For Scanning QR Code.",
+ key: const Key("sendViewScanQrButtonKey"),
+ onTap: _scanQr,
+ child: const QrCodeIcon(),
),
- onTap: () {
- _slateController.text = "";
- setState(() {
- _slateToggleFlag = false;
- });
- },
- child: const XIcon(),
- )
- : TextFieldIconButton(
- key: const Key(
- "slateFinalizePasteFieldButtonKey",
- ),
- onTap: _pasteSlatepack,
- child:
- _slateController.text.isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
- if (_slateController.text.isEmpty)
- TextFieldIconButton(
- semanticsLabel:
- "Scan QR Button. Opens Camera For Scanning QR Code.",
- key: const Key("sendViewScanQrButtonKey"),
- onTap: _scanQr,
- child: const QrCodeIcon(),
- ),
- ],
+ ],
+ ),
+ ),
),
),
- ),
- ),
),
),
Util.isDesktop ? const SizedBox(height: 24) : const Spacer(),
diff --git a/lib/pages/monkey/monkey_view.dart b/lib/pages/monkey/monkey_view.dart
index 49329cc986..e16f8d6353 100644
--- a/lib/pages/monkey/monkey_view.dart
+++ b/lib/pages/monkey/monkey_view.dart
@@ -6,6 +6,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
+import 'package:saf_stream/saf_stream.dart';
+import 'package:saf_util/saf_util.dart';
import '../../notifications/show_flush_bar.dart';
import '../../providers/global/wallets_provider.dart';
@@ -13,8 +15,8 @@ import '../../services/monkey_service.dart';
import '../../themes/coin_icon_provider.dart';
import '../../themes/stack_colors.dart';
import '../../utilities/assets.dart';
+import '../../utilities/fs.dart';
import '../../utilities/show_loading.dart';
-import '../../utilities/stack_file_system.dart';
import '../../utilities/text_styles.dart';
import '../../utilities/util.dart';
import '../../wallets/isar/providers/wallet_info_provider.dart';
@@ -51,13 +53,13 @@ class _MonkeyViewState extends ConsumerState {
.updateMonkeyImageBytes(monKeyBytes.toList());
}
- Future _getDocsDir() async {
+ Future _getDocsDir() async {
try {
if (Platform.isAndroid) {
- return await StackFileSystem.wtfAndroidDocumentsPath();
+ return await FS.pickDirectory();
}
- return await getApplicationDocumentsDirectory();
+ return (await getApplicationDocumentsDirectory()).path;
} catch (_) {
return null;
}
@@ -70,27 +72,40 @@ class _MonkeyViewState extends ConsumerState {
bool isPNG = false,
bool overwrite = false,
}) async {
- final dir = await _getDocsDir();
- if (dir == null) {
- throw Exception("Failed to get documents directory to save monKey image");
+ final dirPath = await _getDocsDir();
+ if (dirPath == null) {
+ throw Exception("Failed to get directory path to save monKey image");
}
- final address =
- await ref
- .read(pWallets)
- .getWallet(walletId)
- .getCurrentReceivingAddress();
- String filePath = path.join(dir.path, "monkey_${address?.value}");
+ final address = await ref
+ .read(pWallets)
+ .getWallet(walletId)
+ .getCurrentReceivingAddress();
- filePath += isPNG ? ".png" : ".svg";
+ final fileName = "monkey_${address?.value}${isPNG ? ".png" : ".svg"}";
+ final filePath = path.join(dirPath, fileName);
- final File imgFile = File(filePath);
+ if (Platform.isAndroid) {
+ if (!overwrite && await SafUtil().exists(filePath, false)) {
+ throw Exception("File already exists");
+ }
+
+ await SafStream().writeFileBytes(
+ dirPath,
+ fileName,
+ isPNG ? "png" : "svg",
+ bytes,
+ );
+ } else {
+ final File imgFile = File(filePath);
+
+ if (imgFile.existsSync() && !overwrite) {
+ throw Exception("File already exists");
+ }
- if (imgFile.existsSync() && !overwrite) {
- throw Exception("File already exists");
+ await imgFile.writeAsBytes(bytes);
}
- await imgFile.writeAsBytes(bytes);
_monkeyPath = filePath;
}
@@ -113,313 +128,296 @@ class _MonkeyViewState extends ConsumerState {
return Background(
child: ConditionalParent(
condition: isDesktop,
- builder:
- (child) => DesktopScaffold(
- appBar: DesktopAppBar(
- background: Theme.of(context).extension()!.popupBG,
- leading: Expanded(
- child: Row(
- children: [
- const SizedBox(width: 32),
- AppBarIconButton(
- size: 32,
- color:
- Theme.of(
- context,
- ).extension()!.textFieldDefaultBG,
- shadows: const [],
- icon: SvgPicture.asset(
- Assets.svg.arrowLeft,
- width: 18,
- height: 18,
- color:
- Theme.of(
- context,
- ).extension()!.topNavIconPrimary,
- ),
- onPressed: Navigator.of(context).pop,
- ),
- const SizedBox(width: 15),
- SvgPicture.asset(
- Assets.svg.monkey,
- width: 32,
- height: 32,
- color:
- Theme.of(
- context,
- ).extension()!.textSubtitle1,
- ),
- const SizedBox(width: 12),
- Text("MonKey", style: STextStyles.desktopH3(context)),
- ],
+ builder: (child) => DesktopScaffold(
+ appBar: DesktopAppBar(
+ background: Theme.of(context).extension()!.popupBG,
+ leading: Expanded(
+ child: Row(
+ children: [
+ const SizedBox(width: 32),
+ AppBarIconButton(
+ size: 32,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
+ shadows: const [],
+ icon: SvgPicture.asset(
+ Assets.svg.arrowLeft,
+ width: 18,
+ height: 18,
+ color: Theme.of(
+ context,
+ ).extension()!.topNavIconPrimary,
+ ),
+ onPressed: Navigator.of(context).pop,
),
- ),
- trailing: RawMaterialButton(
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(1000),
+ const SizedBox(width: 15),
+ SvgPicture.asset(
+ Assets.svg.monkey,
+ width: 32,
+ height: 32,
+ color: Theme.of(
+ context,
+ ).extension()!.textSubtitle1,
),
- onPressed: () {
- showDialog(
- context: context,
- useSafeArea: false,
- barrierDismissible: true,
- builder: (context) {
- return DesktopDialog(
- maxHeight: double.infinity,
- child: Column(
+ const SizedBox(width: 12),
+ Text("MonKey", style: STextStyles.desktopH3(context)),
+ ],
+ ),
+ ),
+ trailing: RawMaterialButton(
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(1000),
+ ),
+ onPressed: () {
+ showDialog(
+ context: context,
+ useSafeArea: false,
+ barrierDismissible: true,
+ builder: (context) {
+ return DesktopDialog(
+ maxHeight: double.infinity,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Row(
- mainAxisAlignment:
- MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "About MonKeys",
- style: STextStyles.desktopH3(context),
- ),
- ),
- const DesktopDialogCloseButton(),
- ],
- ),
- Text(
- "A MonKey is a visual representation of your Banano address.",
- style: STextStyles.desktopTextMedium(
- context,
- ).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.textDark3,
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "About MonKeys",
+ style: STextStyles.desktopH3(context),
),
),
- Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- Padding(
- padding: const EdgeInsets.all(32),
- child: PrimaryButton(
- width: 272.5,
- label: "OK",
- onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- ),
- ],
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Text(
+ "A MonKey is a visual representation of your Banano address.",
+ style: STextStyles.desktopTextMedium(context)
+ .copyWith(
+ color: Theme.of(
+ context,
+ ).extension()!.textDark3,
+ ),
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Padding(
+ padding: const EdgeInsets.all(32),
+ child: PrimaryButton(
+ width: 272.5,
+ label: "OK",
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
),
],
),
- );
- },
+ ],
+ ),
);
},
- child: Padding(
- padding: const EdgeInsets.symmetric(
- vertical: 19,
- horizontal: 32,
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: 19,
+ horizontal: 32,
+ ),
+ child: Row(
+ children: [
+ SvgPicture.asset(
+ Assets.svg.circleQuestion,
+ width: 20,
+ height: 20,
+ color: Theme.of(
+ context,
+ ).extension()!.customTextButtonEnabledText,
),
- child: Row(
- children: [
- SvgPicture.asset(
- Assets.svg.circleQuestion,
- width: 20,
- height: 20,
- color:
- Theme.of(context)
- .extension()!
- .customTextButtonEnabledText,
- ),
- const SizedBox(width: 8),
- Text(
- "What is MonKey?",
- style: STextStyles.desktopMenuItemSelected(
- context,
- ).copyWith(
- color:
- Theme.of(context)
- .extension()!
- .customTextButtonEnabledText,
+ const SizedBox(width: 8),
+ Text(
+ "What is MonKey?",
+ style: STextStyles.desktopMenuItemSelected(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .customTextButtonEnabledText,
),
- ),
- ],
),
- ),
+ ],
),
- useSpacers: false,
- isCompactHeight: true,
),
- body: child,
),
+ useSpacers: false,
+ isCompactHeight: true,
+ ),
+ body: child,
+ ),
child: ConditionalParent(
condition: !isDesktop,
- builder:
- (child) => Scaffold(
- appBar: AppBar(
- leading: AppBarBackButton(
+ builder: (child) => Scaffold(
+ appBar: AppBar(
+ leading: AppBarBackButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ title: Text("MonKey", style: STextStyles.navBarTitle(context)),
+ actions: [
+ AspectRatio(
+ aspectRatio: 1,
+ child: AppBarIconButton(
+ icon: SvgPicture.asset(Assets.svg.circleQuestion),
onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- title: Text(
- "MonKey",
- style: STextStyles.navBarTitle(context),
- ),
- actions: [
- AspectRatio(
- aspectRatio: 1,
- child: AppBarIconButton(
- icon: SvgPicture.asset(Assets.svg.circleQuestion),
- onPressed: () {
- showDialog(
- context: context,
- useSafeArea: false,
- barrierDismissible: true,
- builder: (context) {
- return const StackOkDialog(
- title: "About MonKeys",
- message:
- "A MonKey is a visual representation of your Banano address.",
- );
- },
+ showDialog(
+ context: context,
+ useSafeArea: false,
+ barrierDismissible: true,
+ builder: (context) {
+ return const StackOkDialog(
+ title: "About MonKeys",
+ message:
+ "A MonKey is a visual representation of your Banano address.",
);
},
- ),
- ),
- ],
+ );
+ },
+ ),
),
- body: SafeArea(child: child),
- ),
+ ],
+ ),
+ body: SafeArea(child: child),
+ ),
child: ConditionalParent(
condition: isDesktop,
builder: (child) => SizedBox(width: 318, child: child),
child: ConditionalParent(
condition: imageBytes != null,
- builder:
- (_) => Column(
- children: [
- isDesktop
- ? const SizedBox(height: 50)
- : const Spacer(flex: 1),
- if (imageBytes != null)
- SizedBox(
- width: 300,
- height: 300,
- child: SvgPicture.memory(
- Uint8List.fromList(imageBytes!),
- ),
- ),
- isDesktop
- ? const SizedBox(height: 50)
- : const Spacer(flex: 1),
- Padding(
- padding: const EdgeInsets.all(16.0),
- child: Column(
- children: [
- SecondaryButton(
- label: "Save as SVG",
- onPressed: () async {
- bool didError = false;
- await showLoading(
- whileFuture: Future.wait([
- _saveMonKeyToFile(
- bytes: Uint8List.fromList(
- (wallet as BananoWallet)
- .getMonkeyImageBytes()!,
- ),
- ),
- Future.delayed(
- const Duration(seconds: 2),
- ),
- ]),
+ builder: (_) => Column(
+ children: [
+ isDesktop
+ ? const SizedBox(height: 50)
+ : const Spacer(flex: 1),
+ if (imageBytes != null)
+ SizedBox(
+ width: 300,
+ height: 300,
+ child: SvgPicture.memory(Uint8List.fromList(imageBytes!)),
+ ),
+ isDesktop
+ ? const SizedBox(height: 50)
+ : const Spacer(flex: 1),
+ Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ children: [
+ SecondaryButton(
+ label: "Save as SVG",
+ onPressed: () async {
+ bool didError = false;
+ await showLoading(
+ whileFuture: Future.wait([
+ _saveMonKeyToFile(
+ bytes: Uint8List.fromList(
+ (wallet as BananoWallet)
+ .getMonkeyImageBytes()!,
+ ),
+ ),
+ Future.delayed(
+ const Duration(seconds: 2),
+ ),
+ ]),
+ context: context,
+ rootNavigator: Util.isDesktop,
+ message: "Saving MonKey svg",
+ onException: (e) {
+ didError = true;
+ String msg = e.toString();
+ while (msg.isNotEmpty &&
+ msg.startsWith("Exception:")) {
+ msg = msg.substring(10).trim();
+ }
+ showFloatingFlushBar(
+ type: FlushBarType.warning,
+ message: msg,
context: context,
- rootNavigator: Util.isDesktop,
- message: "Saving MonKey svg",
- onException: (e) {
- didError = true;
- String msg = e.toString();
- while (msg.isNotEmpty &&
- msg.startsWith("Exception:")) {
- msg = msg.substring(10).trim();
- }
- showFloatingFlushBar(
- type: FlushBarType.warning,
- message: msg,
- context: context,
- );
- },
);
+ },
+ );
- if (!didError && mounted) {
- await showFloatingFlushBar(
- type: FlushBarType.success,
- message:
- "SVG MonKey image saved to $_monkeyPath",
- context: context,
- );
+ if (!didError && mounted) {
+ await showFloatingFlushBar(
+ type: FlushBarType.success,
+ message:
+ "SVG MonKey image saved to $_monkeyPath",
+ context: context,
+ );
+ }
+ },
+ ),
+ const SizedBox(height: 12),
+ SecondaryButton(
+ label: "Download as PNG",
+ onPressed: () async {
+ bool didError = false;
+ await showLoading(
+ whileFuture: Future.wait([
+ wallet.getCurrentReceivingAddress().then(
+ (address) async => await ref
+ .read(pMonKeyService)
+ .fetchMonKey(
+ address: address!.value,
+ png: true,
+ )
+ .then(
+ (monKeyBytes) async =>
+ await _saveMonKeyToFile(
+ bytes: monKeyBytes,
+ isPNG: true,
+ ),
+ ),
+ ),
+ Future.delayed(
+ const Duration(seconds: 2),
+ ),
+ ]),
+ context: context,
+ rootNavigator: Util.isDesktop,
+ message: "Downloading MonKey png",
+ onException: (e) {
+ didError = true;
+ String msg = e.toString();
+ while (msg.isNotEmpty &&
+ msg.startsWith("Exception:")) {
+ msg = msg.substring(10).trim();
}
- },
- ),
- const SizedBox(height: 12),
- SecondaryButton(
- label: "Download as PNG",
- onPressed: () async {
- bool didError = false;
- await showLoading(
- whileFuture: Future.wait([
- wallet.getCurrentReceivingAddress().then(
- (address) async => await ref
- .read(pMonKeyService)
- .fetchMonKey(
- address: address!.value,
- png: true,
- )
- .then(
- (monKeyBytes) async =>
- await _saveMonKeyToFile(
- bytes: monKeyBytes,
- isPNG: true,
- ),
- ),
- ),
- Future.delayed(
- const Duration(seconds: 2),
- ),
- ]),
+ showFloatingFlushBar(
+ type: FlushBarType.warning,
+ message: msg,
context: context,
- rootNavigator: Util.isDesktop,
- message: "Downloading MonKey png",
- onException: (e) {
- didError = true;
- String msg = e.toString();
- while (msg.isNotEmpty &&
- msg.startsWith("Exception:")) {
- msg = msg.substring(10).trim();
- }
- showFloatingFlushBar(
- type: FlushBarType.warning,
- message: msg,
- context: context,
- );
- },
);
-
- if (!didError && mounted) {
- await showFloatingFlushBar(
- type: FlushBarType.success,
- message:
- "PNG MonKey image saved to $_monkeyPath",
- context: context,
- );
- }
},
- ),
- ],
+ );
+
+ if (!didError && mounted) {
+ await showFloatingFlushBar(
+ type: FlushBarType.success,
+ message:
+ "PNG MonKey image saved to $_monkeyPath",
+ context: context,
+ );
+ }
+ },
),
- ),
- // child,
- ],
+ ],
+ ),
),
+ // child,
+ ],
+ ),
child: Column(
children: [
isDesktop
@@ -440,10 +438,9 @@ class _MonkeyViewState extends ConsumerState {
Text(
"You do not have a MonKey yet. \nFetch yours now!",
style: STextStyles.smallMed14(context).copyWith(
- color:
- Theme.of(
- context,
- ).extension()!.textDark3,
+ color: Theme.of(
+ context,
+ ).extension()!.textDark3,
),
textAlign: TextAlign.center,
),
@@ -489,8 +486,8 @@ class _MonkeyViewState extends ConsumerState {
},
);
- imageBytes =
- (wallet as BananoWallet).getMonkeyImageBytes();
+ imageBytes = (wallet as BananoWallet)
+ .getMonkeyImageBytes();
if (imageBytes != null) {
setState(() {});
diff --git a/lib/pages/namecoin_names/sub_widgets/transfer_option_widget.dart b/lib/pages/namecoin_names/sub_widgets/transfer_option_widget.dart
index 89be6129cb..5dbc649bcd 100644
--- a/lib/pages/namecoin_names/sub_widgets/transfer_option_widget.dart
+++ b/lib/pages/namecoin_names/sub_widgets/transfer_option_widget.dart
@@ -169,16 +169,15 @@ class _TransferOptionWidgetState extends ConsumerState {
if (Util.isDesktop) {
await showDialog(
context: context,
- builder:
- (context) => SDialog(
- child: SizedBox(
- width: 580,
- child: ConfirmNameTransactionView(
- txData: txData,
- walletId: widget.walletId,
- ),
- ),
+ builder: (context) => SDialog(
+ child: SizedBox(
+ width: 580,
+ child: ConfirmNameTransactionView(
+ txData: txData,
+ walletId: widget.walletId,
),
+ ),
+ ),
);
} else {
await Navigator.of(context).pushNamed(
@@ -203,13 +202,12 @@ class _TransferOptionWidgetState extends ConsumerState {
await showDialog(
context: context,
- builder:
- (_) => StackOkDialog(
- title: "Error",
- message: err,
- desktopPopRootNavigator: Util.isDesktop,
- maxWidth: Util.isDesktop ? 600 : null,
- ),
+ builder: (_) => StackOkDialog(
+ title: "Error",
+ message: err,
+ desktopPopRootNavigator: Util.isDesktop,
+ maxWidth: Util.isDesktop ? 600 : null,
+ ),
);
}
} finally {
@@ -238,12 +236,14 @@ class _TransferOptionWidgetState extends ConsumerState {
}
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
+
final coin = ref.read(pWalletCoin(walletId));
Logging.instance.d("qrResult content: ${qrResult.rawContent}");
final paymentData = AddressUtils.parsePaymentUri(
- qrResult.rawContent,
+ qrResult.rawContent!,
logging: Logging.instance,
);
@@ -257,7 +257,7 @@ class _TransferOptionWidgetState extends ConsumerState {
// now check for non standard encoded basic address
} else {
- _address = qrResult.rawContent.split("\n").first.trim();
+ _address = qrResult.rawContent!.split("\n").first.trim();
_addressController.text = _address ?? "";
_setValidAddressProviders(_address);
@@ -313,8 +313,9 @@ class _TransferOptionWidgetState extends ConsumerState {
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
- crossAxisAlignment:
- Util.isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.center,
+ crossAxisAlignment: Util.isDesktop
+ ? CrossAxisAlignment.start
+ : CrossAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(
@@ -338,121 +339,120 @@ class _TransferOptionWidgetState extends ConsumerState {
},
focusNode: _addressFocusNode,
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Enter ${ref.watch(pWalletCoin(walletId)).ticker} address",
- _addressFocusNode,
- context,
- ).copyWith(
- contentPadding: const EdgeInsets.only(
- left: 16,
- top: 6,
- bottom: 8,
- right: 5,
- ),
- suffixIcon: Padding(
- padding:
- _addressController.text.isEmpty
+ decoration:
+ standardInputDecoration(
+ "Enter ${ref.watch(pWalletCoin(walletId)).ticker} address",
+ _addressFocusNode,
+ context,
+ ).copyWith(
+ contentPadding: const EdgeInsets.only(
+ left: 16,
+ top: 6,
+ bottom: 8,
+ right: 5,
+ ),
+ suffixIcon: Padding(
+ padding: _addressController.text.isEmpty
? const EdgeInsets.only(right: 8)
: const EdgeInsets.only(right: 0),
- child: UnconstrainedBox(
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- _addressController.text.isNotEmpty
- ? TextFieldIconButton(
- semanticsLabel:
- "Clear Button. Clears The Address Field Input.",
- key: const Key(
- "nameTransferClearAddressFieldButtonKey",
+ child: UnconstrainedBox(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ _addressController.text.isNotEmpty
+ ? TextFieldIconButton(
+ semanticsLabel:
+ "Clear Button. Clears The Address Field Input.",
+ key: const Key(
+ "nameTransferClearAddressFieldButtonKey",
+ ),
+ onTap: () {
+ _addressController.text = "";
+ _address = "";
+ _setValidAddressProviders(_address);
+ setState(() {});
+ },
+ child: const XIcon(),
+ )
+ : TextFieldIconButton(
+ semanticsLabel:
+ "Paste Button. Pastes From Clipboard To Address Field Input.",
+ key: const Key(
+ "nameTransferPasteAddressFieldButtonKey",
+ ),
+ onTap: () async {
+ final ClipboardData? data = await clipboard
+ .getData(Clipboard.kTextPlain);
+ if (data?.text != null &&
+ data!.text!.isNotEmpty) {
+ String content = data.text!.trim();
+ if (content.contains("\n")) {
+ content = content.substring(
+ 0,
+ content.indexOf("\n"),
+ );
+ }
+
+ _addressController.text = content.trim();
+ _address = content.trim();
+
+ _setValidAddressProviders(_address);
+ }
+ },
+ child: _addressController.text.isEmpty
+ ? const ClipboardIcon()
+ : const XIcon(),
+ ),
+ if (_addressController.text.isEmpty)
+ TextFieldIconButton(
+ semanticsLabel:
+ "Address Book Button. Opens Address Book For Address Field.",
+ key: const Key(
+ "nameTransferAddressBookButtonKey",
+ ),
+ onTap: () {
+ Navigator.of(context).pushNamed(
+ AddressBookView.routeName,
+ arguments: ref.read(pWalletCoin(walletId)),
+ );
+ },
+ child: const AddressBookIcon(),
),
- onTap: () {
- _addressController.text = "";
- _address = "";
- _setValidAddressProviders(_address);
- setState(() {});
- },
- child: const XIcon(),
- )
- : TextFieldIconButton(
- semanticsLabel:
- "Paste Button. Pastes From Clipboard To Address Field Input.",
- key: const Key(
- "nameTransferPasteAddressFieldButtonKey",
+ if (_addressController.text.isEmpty)
+ TextFieldIconButton(
+ semanticsLabel:
+ "Scan QR Button. Opens Camera For Scanning QR Code.",
+ key: const Key("nameTransferScanQrButtonKey"),
+ onTap: _scanQr,
+ child: const QrCodeIcon(),
),
- onTap: () async {
- final ClipboardData? data = await clipboard
- .getData(Clipboard.kTextPlain);
- if (data?.text != null &&
- data!.text!.isNotEmpty) {
- String content = data.text!.trim();
- if (content.contains("\n")) {
- content = content.substring(
- 0,
- content.indexOf("\n"),
- );
- }
-
- _addressController.text = content.trim();
- _address = content.trim();
-
- _setValidAddressProviders(_address);
- }
- },
- child:
- _addressController.text.isEmpty
- ? const ClipboardIcon()
- : const XIcon(),
- ),
- if (_addressController.text.isEmpty)
- TextFieldIconButton(
- semanticsLabel:
- "Address Book Button. Opens Address Book For Address Field.",
- key: const Key("nameTransferAddressBookButtonKey"),
- onTap: () {
- Navigator.of(context).pushNamed(
- AddressBookView.routeName,
- arguments: ref.read(pWalletCoin(walletId)),
- );
- },
- child: const AddressBookIcon(),
- ),
- if (_addressController.text.isEmpty)
- TextFieldIconButton(
- semanticsLabel:
- "Scan QR Button. Opens Camera For Scanning QR Code.",
- key: const Key("nameTransferScanQrButtonKey"),
- onTap: _scanQr,
- child: const QrCodeIcon(),
- ),
- ],
+ ],
+ ),
+ ),
),
),
- ),
- ),
),
),
SizedBox(height: Util.isDesktop ? 42 : 16),
if (!Util.isDesktop) const Spacer(),
ConditionalParent(
condition: Util.isDesktop,
- builder:
- (child) => Row(
- children: [
- Expanded(
- child: SecondaryButton(
- label: "Cancel",
- buttonHeight: ButtonHeight.l,
- onPressed:
- Navigator.of(
- context,
- rootNavigator: Util.isDesktop,
- ).pop,
- ),
- ),
- const SizedBox(width: 16),
- Expanded(child: child),
- ],
+ builder: (child) => Row(
+ children: [
+ Expanded(
+ child: SecondaryButton(
+ label: "Cancel",
+ buttonHeight: ButtonHeight.l,
+ onPressed: Navigator.of(
+ context,
+ rootNavigator: Util.isDesktop,
+ ).pop,
+ ),
),
+ const SizedBox(width: 16),
+ Expanded(child: child),
+ ],
+ ),
child: PrimaryButton(
label: "Transfer",
enabled: _enableButton,
diff --git a/lib/pages/ordinals/ordinal_details_view.dart b/lib/pages/ordinals/ordinal_details_view.dart
index 996f20db67..7ea7c2d342 100644
--- a/lib/pages/ordinals/ordinal_details_view.dart
+++ b/lib/pages/ordinals/ordinal_details_view.dart
@@ -7,6 +7,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
+import 'package:saf_stream/saf_stream.dart';
+import 'package:saf_util/saf_util.dart';
import '../../app_config.dart';
import '../../models/isar/models/blockchain_data/utxo.dart';
@@ -21,8 +23,8 @@ import '../../utilities/amount/amount.dart';
import '../../utilities/amount/amount_formatter.dart';
import '../../utilities/assets.dart';
import '../../utilities/constants.dart';
+import '../../utilities/fs.dart';
import '../../utilities/show_loading.dart';
-import '../../utilities/stack_file_system.dart';
import '../../utilities/text_styles.dart';
import '../../wallets/isar/providers/wallet_info_provider.dart';
import '../../widgets/background.dart';
@@ -210,6 +212,18 @@ class _OrdinalImageGroup extends ConsumerWidget {
static const _spacing = 12.0;
+ Future _getDocsDir() async {
+ try {
+ if (Platform.isAndroid) {
+ return await FS.pickDirectory();
+ }
+
+ return (await getApplicationDocumentsDirectory()).path;
+ } catch (_) {
+ return null;
+ }
+ }
+
Future _savePngToFile(WidgetRef ref) async {
final HTTP client = HTTP();
@@ -230,21 +244,36 @@ class _OrdinalImageGroup extends ConsumerWidget {
final bytes = response.bodyBytes;
- final dir = Platform.isAndroid
- ? await StackFileSystem.wtfAndroidDocumentsPath()
- : await getApplicationDocumentsDirectory();
- final filePath = path.join(
- dir.path,
- "ordinal_${ordinal.inscriptionNumber}.png",
- );
+ final dirPath = await _getDocsDir();
+ if (dirPath == null) {
+ throw Exception("Failed to get directory path to save ordinal image");
+ }
+
+ final fileName = "ordinal_${ordinal.inscriptionNumber}.png";
+
+ final filePath = path.join(dirPath, fileName);
+
+ if (Platform.isAndroid) {
+ if (await SafUtil().exists(filePath, false)) {
+ throw Exception("File already exists");
+ }
+
+ await SafStream().writeFileBytes(
+ dirPath,
+ fileName,
+ "png",
+ Uint8List.fromList(bytes),
+ );
+ } else {
+ final File imgFile = File(filePath);
- final File imgFile = File(filePath);
+ if (imgFile.existsSync()) {
+ throw Exception("File already exists");
+ }
- if (imgFile.existsSync()) {
- throw Exception("File already exists");
+ await imgFile.writeAsBytes(bytes);
}
- await imgFile.writeAsBytes(bytes);
return filePath;
}
diff --git a/lib/pages/paynym/add_new_paynym_follow_view.dart b/lib/pages/paynym/add_new_paynym_follow_view.dart
index 85e4c3ac73..37c5b1a8cf 100644
--- a/lib/pages/paynym/add_new_paynym_follow_view.dart
+++ b/lib/pages/paynym/add_new_paynym_follow_view.dart
@@ -122,8 +122,9 @@ class _AddNewPaynymFollowViewState
}
final qrResult = await ref.read(pBarcodeScanner).scan(context: context);
+ if (qrResult.rawContent == null) return;
- final pCodeString = qrResult.rawContent;
+ final pCodeString = qrResult.rawContent!;
_searchString = pCodeString;
@@ -173,93 +174,82 @@ class _AddNewPaynymFollowViewState
return ConditionalParent(
condition: !isDesktop,
- builder:
- (child) => MasterScaffold(
- isDesktop: isDesktop,
- appBar: AppBar(
- leading: AppBarBackButton(
- onPressed: () {
- Navigator.of(context).pop();
- },
- ),
- titleSpacing: 0,
- title: Text(
- "New follow",
- style: STextStyles.navBarTitle(context),
- overflow: TextOverflow.ellipsis,
- ),
- ),
- body: SafeArea(
- child: LayoutBuilder(
- builder:
- (context, constraints) => SingleChildScrollView(
- child: ConstrainedBox(
- constraints: BoxConstraints(
- minHeight: constraints.maxHeight,
- ),
- child: IntrinsicHeight(
- child: Padding(
- padding: const EdgeInsets.all(16),
- child: child,
- ),
- ),
- ),
- ),
+ builder: (child) => MasterScaffold(
+ isDesktop: isDesktop,
+ appBar: AppBar(
+ leading: AppBarBackButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ titleSpacing: 0,
+ title: Text(
+ "New follow",
+ style: STextStyles.navBarTitle(context),
+ overflow: TextOverflow.ellipsis,
+ ),
+ ),
+ body: SafeArea(
+ child: LayoutBuilder(
+ builder: (context, constraints) => SingleChildScrollView(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(minHeight: constraints.maxHeight),
+ child: IntrinsicHeight(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: child,
+ ),
+ ),
),
),
),
+ ),
+ ),
child: ConditionalParent(
condition: isDesktop,
- builder:
- (child) => DesktopDialog(
- maxWidth: 580,
- maxHeight: double.infinity,
- child: Column(
+ builder: (child) => DesktopDialog(
+ maxWidth: 580,
+ maxHeight: double.infinity,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "New follow",
- style: STextStyles.desktopH3(context),
- ),
- ),
- const DesktopDialogCloseButton(),
- ],
- ),
Padding(
- padding: const EdgeInsets.only(
- left: 32,
- right: 32,
- bottom: 32,
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "New follow",
+ style: STextStyles.desktopH3(context),
),
- child: child,
),
+ const DesktopDialogCloseButton(),
],
),
- ),
+ Padding(
+ padding: const EdgeInsets.only(left: 32, right: 32, bottom: 32),
+ child: child,
+ ),
+ ],
+ ),
+ ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
Text(
"Featured PayNyms",
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.sectionLabelMedium12(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.sectionLabelMedium12(context),
),
const SizedBox(height: 12),
FeaturedPaynymsWidget(walletId: widget.walletId),
const SizedBox(height: 24),
Text(
"Add new",
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.sectionLabelMedium12(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.sectionLabelMedium12(context),
),
const SizedBox(height: 12),
if (isDesktop)
@@ -270,10 +260,9 @@ class _AddNewPaynymFollowViewState
children: [
RoundedContainer(
padding: const EdgeInsets.all(0),
- color:
- Theme.of(
- context,
- ).extension()!.textFieldDefaultBG,
+ color: Theme.of(
+ context,
+ ).extension()!.textFieldDefaultBG,
height: 56,
child: Center(
child: TextField(
@@ -286,15 +275,15 @@ class _AddNewPaynymFollowViewState
_searchString = value;
});
},
- style: STextStyles.desktopTextExtraExtraSmall(
- context,
- ).copyWith(
- color:
- Theme.of(context)
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ).copyWith(
+ color: Theme.of(context)
.extension()!
.textFieldActiveText,
- // height: 1.8,
- ),
+ // height: 1.8,
+ ),
decoration: InputDecoration(
hintText: "Paste payment code",
hoverColor: Colors.transparent,
@@ -315,38 +304,32 @@ class _AddNewPaynymFollowViewState
children: [
_searchController.text.isNotEmpty
? TextFieldIconButton(
- onTap: _clear,
- child: RoundedContainer(
- padding: const EdgeInsets.all(
- 8,
+ onTap: _clear,
+ child: RoundedContainer(
+ padding: const EdgeInsets.all(
+ 8,
+ ),
+ color: Theme.of(context)
+ .extension()!
+ .buttonBackSecondary,
+ child: const XIcon(),
),
- color:
- Theme.of(context)
- .extension<
- StackColors
- >()!
- .buttonBackSecondary,
- child: const XIcon(),
- ),
- )
+ )
: TextFieldIconButton(
- key: const Key(
- "paynymPasteAddressFieldButtonKey",
- ),
- onTap: _paste,
- child: RoundedContainer(
- padding: const EdgeInsets.all(
- 8,
+ key: const Key(
+ "paynymPasteAddressFieldButtonKey",
+ ),
+ onTap: _paste,
+ child: RoundedContainer(
+ padding: const EdgeInsets.all(
+ 8,
+ ),
+ color: Theme.of(context)
+ .extension()!
+ .buttonBackSecondary,
+ child: const ClipboardIcon(),
),
- color:
- Theme.of(context)
- .extension<
- StackColors
- >()!
- .buttonBackSecondary,
- child: const ClipboardIcon(),
),
- ),
TextFieldIconButton(
key: const Key(
"paynymScanQrButtonKey",
@@ -354,10 +337,9 @@ class _AddNewPaynymFollowViewState
onTap: _scanQr,
child: RoundedContainer(
padding: const EdgeInsets.all(8),
- color:
- Theme.of(context)
- .extension()!
- .buttonBackSecondary,
+ color: Theme.of(context)
+ .extension()!
+ .buttonBackSecondary,
child: const QrCodeIcon(),
),
),
@@ -392,39 +374,40 @@ class _AddNewPaynymFollowViewState
});
},
style: STextStyles.field(context),
- decoration: standardInputDecoration(
- "Paste payment code",
- searchFieldFocusNode,
- context,
- desktopMed: isDesktop,
- ).copyWith(
- suffixIcon: Padding(
- padding: const EdgeInsets.only(right: 8),
- child: UnconstrainedBox(
- child: Row(
- children: [
- _searchController.text.isNotEmpty
- ? TextFieldIconButton(
- onTap: _clear,
- child: const XIcon(),
- )
- : TextFieldIconButton(
- key: const Key(
- "paynymPasteAddressFieldButtonKey",
- ),
- onTap: _paste,
- child: const ClipboardIcon(),
+ decoration:
+ standardInputDecoration(
+ "Paste payment code",
+ searchFieldFocusNode,
+ context,
+ desktopMed: isDesktop,
+ ).copyWith(
+ suffixIcon: Padding(
+ padding: const EdgeInsets.only(right: 8),
+ child: UnconstrainedBox(
+ child: Row(
+ children: [
+ _searchController.text.isNotEmpty
+ ? TextFieldIconButton(
+ onTap: _clear,
+ child: const XIcon(),
+ )
+ : TextFieldIconButton(
+ key: const Key(
+ "paynymPasteAddressFieldButtonKey",
+ ),
+ onTap: _paste,
+ child: const ClipboardIcon(),
+ ),
+ TextFieldIconButton(
+ key: const Key("paynymScanQrButtonKey"),
+ onTap: _scanQr,
+ child: const QrCodeIcon(),
),
- TextFieldIconButton(
- key: const Key("paynymScanQrButtonKey"),
- onTap: _scanQr,
- child: const QrCodeIcon(),
+ ],
),
- ],
+ ),
),
),
- ),
- ),
),
),
if (!isDesktop) const SizedBox(height: 12),
@@ -433,21 +416,19 @@ class _AddNewPaynymFollowViewState
if (_didSearch) const SizedBox(height: 20),
if (_didSearch && _searchResult == null)
RoundedWhiteContainer(
- borderColor:
- isDesktop
- ? Theme.of(
- context,
- ).extension()!.backgroundAppBar
- : null,
+ borderColor: isDesktop
+ ? Theme.of(
+ context,
+ ).extension()!.backgroundAppBar
+ : null,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Nothing found. Please check the payment code.",
- style:
- isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.label(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.label(context),
),
],
),
@@ -455,12 +436,11 @@ class _AddNewPaynymFollowViewState
if (_didSearch && _searchResult != null)
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
- borderColor:
- isDesktop
- ? Theme.of(
- context,
- ).extension()!.backgroundAppBar
- : null,
+ borderColor: isDesktop
+ ? Theme.of(
+ context,
+ ).extension()!.backgroundAppBar
+ : null,
child: PaynymCard(
key: UniqueKey(),
label: _searchResult!.nymName,
diff --git a/lib/pages/receive_view/addresses/address_card.dart b/lib/pages/receive_view/addresses/address_card.dart
index 901cd578be..f8b6ec8067 100644
--- a/lib/pages/receive_view/addresses/address_card.dart
+++ b/lib/pages/receive_view/addresses/address_card.dart
@@ -50,6 +50,7 @@ class AddressCard extends ConsumerStatefulWidget {
required this.coin,
this.onPressed,
this.clipboard = const ClipboardWrapper(),
+ this.compact = false,
});
final int addressId;
@@ -57,6 +58,7 @@ class AddressCard extends ConsumerStatefulWidget {
final CryptoCurrency coin;
final ClipboardInterface clipboard;
final VoidCallback? onPressed;
+ final bool compact;
@override
ConsumerState createState() => _AddressCardState();
@@ -142,11 +144,10 @@ class _AddressCardState extends ConsumerState {
@override
void initState() {
- address =
- MainDB.instance.isar.addresses
- .where()
- .idEqualTo(widget.addressId)
- .findFirstSync()!;
+ address = MainDB.instance.isar.addresses
+ .where()
+ .idEqualTo(widget.addressId)
+ .findFirstSync()!;
label = MainDB.instance.getAddressLabelSync(widget.walletId, address.value);
Id? id = label?.id;
@@ -155,12 +156,11 @@ class _AddressCardState extends ConsumerState {
walletId: widget.walletId,
addressString: address.value,
value: "",
- tags:
- address.subType == AddressSubType.receiving
- ? ["receiving"]
- : address.subType == AddressSubType.change
- ? ["change"]
- : null,
+ tags: address.subType == AddressSubType.receiving
+ ? ["receiving"]
+ : address.subType == AddressSubType.change
+ ? ["change"]
+ : null,
);
id = MainDB.instance.putAddressLabelSync(label!);
}
@@ -181,20 +181,19 @@ class _AddressCardState extends ConsumerState {
}
return ConditionalParent(
- condition: isDesktop,
- builder:
- (child) => Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- SvgPicture.file(
- File(ref.watch(coinIconProvider(widget.coin))),
- width: 32,
- height: 32,
- ),
- const SizedBox(width: 12),
- Expanded(child: child),
- ],
+ condition: isDesktop && !widget.compact,
+ builder: (child) => Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SvgPicture.file(
+ File(ref.watch(coinIconProvider(widget.coin))),
+ width: 32,
+ height: 32,
),
+ const SizedBox(width: 12),
+ Expanded(child: child),
+ ],
+ ),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -230,129 +229,124 @@ class _AddressCardState extends ConsumerState {
),
],
),
- const SizedBox(height: 10),
- Row(
- children: [
- CustomTextButton(
- text: "Copy address",
- onTap: () {
- widget.clipboard
- .setData(ClipboardData(text: address.value))
- .then((value) {
- if (context.mounted) {
- unawaited(
- showFloatingFlushBar(
- type: FlushBarType.info,
- message: "Copied to clipboard",
- context: context,
- ),
- );
- }
- });
- },
- ),
- const SizedBox(width: 16),
- CustomTextButton(
- text: "Show QR code",
- onTap: () async {
- await showDialog(
- context: context,
- builder: (_) {
- return StackDialogBase(
- child: Column(
- children: [
- if (label!.value.isNotEmpty)
- Text(
- label!.value,
- style: STextStyles.w600_18(context),
+ if (!widget.compact) const SizedBox(height: 10),
+ if (!widget.compact)
+ Row(
+ children: [
+ CustomTextButton(
+ text: "Copy address",
+ onTap: () {
+ widget.clipboard
+ .setData(ClipboardData(text: address.value))
+ .then((value) {
+ if (context.mounted) {
+ unawaited(
+ showFloatingFlushBar(
+ type: FlushBarType.info,
+ message: "Copied to clipboard",
+ context: context,
),
- if (label!.value.isNotEmpty)
- const SizedBox(height: 8),
- Text(
- address.value,
- style: STextStyles.w500_16(
- context,
- ).copyWith(
- color:
- Theme.of(context)
- .extension()!
- .textSubtitle1,
+ );
+ }
+ });
+ },
+ ),
+ const SizedBox(width: 16),
+ CustomTextButton(
+ text: "Show QR code",
+ onTap: () async {
+ await showDialog(
+ context: context,
+ builder: (_) {
+ return StackDialogBase(
+ child: Column(
+ children: [
+ if (label!.value.isNotEmpty)
+ Text(
+ label!.value,
+ style: STextStyles.w600_18(context),
+ ),
+ if (label!.value.isNotEmpty)
+ const SizedBox(height: 8),
+ Text(
+ address.value,
+ style: STextStyles.w500_16(context)
+ .copyWith(
+ color: Theme.of(context)
+ .extension()!
+ .textSubtitle1,
+ ),
),
- ),
- const SizedBox(height: 16),
- Center(
- child: RepaintBoundary(
- key: _qrKey,
- child: QR(
- data: AddressUtils.buildUriString(
- widget.coin.uriScheme,
- address.value,
- {},
+ const SizedBox(height: 16),
+ Center(
+ child: RepaintBoundary(
+ key: _qrKey,
+ child: QR(
+ data: AddressUtils.buildUriString(
+ widget.coin.uriScheme,
+ address.value,
+ {},
+ ),
+ size: 220,
),
- size: 220,
),
),
- ),
- const SizedBox(height: 16),
- Row(
- children: [
- if (!isDesktop)
- Expanded(
- child: SecondaryButton(
- label: "Share",
- buttonHeight:
- isDesktop
- ? ButtonHeight.l
- : null,
- icon: SvgPicture.asset(
- Assets.svg.share,
- width: 14,
- height: 14,
- color:
- Theme.of(context)
- .extension()!
- .buttonTextSecondary,
+ const SizedBox(height: 16),
+ Row(
+ children: [
+ if (!isDesktop)
+ Expanded(
+ child: SecondaryButton(
+ label: "Share",
+ buttonHeight: isDesktop
+ ? ButtonHeight.l
+ : null,
+ icon: SvgPicture.asset(
+ Assets.svg.share,
+ width: 14,
+ height: 14,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextSecondary,
+ ),
+ onPressed: () async {
+ await _capturePng(false);
+ },
),
- onPressed: () async {
- await _capturePng(false);
- },
),
- ),
- if (isDesktop)
- Expanded(
- child: PrimaryButton(
- buttonHeight:
- isDesktop
- ? ButtonHeight.l
- : null,
- onPressed: () async {
- // TODO: add save functionality instead of share
- // save works on linux at the moment
- await _capturePng(true);
- },
- label: "Save",
- icon: SvgPicture.asset(
- Assets.svg.arrowDown,
- width: 20,
- height: 20,
- color:
- Theme.of(context)
- .extension()!
- .buttonTextPrimary,
+ if (isDesktop)
+ Expanded(
+ child: PrimaryButton(
+ buttonHeight: isDesktop
+ ? ButtonHeight.l
+ : null,
+ onPressed: () async {
+ // TODO: add save functionality instead of share
+ // save works on linux at the moment
+ await _capturePng(true);
+ },
+ label: "Save",
+ icon: SvgPicture.asset(
+ Assets.svg.arrowDown,
+ width: 20,
+ height: 20,
+ color: Theme.of(context)
+ .extension()!
+ .buttonTextPrimary,
+ ),
),
),
- ),
- ],
- ),
- ],
- ),
- );
- },
- );
- },
- ),
- ],
- ),
+ ],
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ],
+ ),
// if (label!.tags != null && label!.tags!.isNotEmpty)
// Wrap(
// spacing: 10,
diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart
index 66b8c88a7c..7ef4f263b5 100644
--- a/lib/pages/receive_view/receive_view.dart
+++ b/lib/pages/receive_view/receive_view.dart
@@ -112,12 +112,10 @@ class _ReceiveViewState extends ConsumerState {
if (mounted) {
await showDialog(
context: context,
- builder:
- (context) => StackOkDialog(
- title: "Slatepack receive error",
- message:
- ex?.toString() ?? "Unexpected result without exception",
- ),
+ builder: (context) => StackOkDialog(
+ title: "Slatepack receive error",
+ message: ex?.toString() ?? "Unexpected result without exception",
+ ),
);
}
return;
@@ -127,27 +125,25 @@ class _ReceiveViewState extends ConsumerState {
final response =
await showDialog<({String responseSlatepack, bool wasEncrypted})>(
context: context,
- builder:
- (context) => SDialog(
- child: MwcSlatepackImportDialog(
- walletId: widget.walletId,
- clipboard: widget.clipboard,
- rawSlatepack: result.raw,
- decoded: result.result,
- slatepackType: result.type,
- ),
- ),
+ builder: (context) => SDialog(
+ child: MwcSlatepackImportDialog(
+ walletId: widget.walletId,
+ clipboard: widget.clipboard,
+ rawSlatepack: result.raw,
+ decoded: result.result,
+ slatepackType: result.type,
+ ),
+ ),
);
if (mounted && response != null) {
await showDialog(
context: context,
barrierDismissible: false,
- builder:
- (context) => SlatepackResponseDialog(
- responseSlatepack: response.responseSlatepack,
- wasEncrypted: response.wasEncrypted,
- ),
+ builder: (context) => SlatepackResponseDialog(
+ responseSlatepack: response.responseSlatepack,
+ wasEncrypted: response.wasEncrypted,
+ ),
);
}
}
@@ -182,17 +178,19 @@ class _ReceiveViewState extends ConsumerState {
final Address? address;
if (wallet is Bip39HDWallet && wallet is! BCashInterface) {
DerivePathType? type;
- if (wallet.isViewOnly && wallet is ExtendedKeysInterface) {
- final voData = await wallet.getViewOnlyWalletData();
+ if (wallet.isViewOnly &&
+ wallet is ExtendedKeysInterface &&
+ wallet.viewOnlyType != .spark) {
+ final voData =
+ await wallet.getViewOnlyWalletData()
+ as ExtendedKeysViewOnlyWalletData;
for (final t in wallet.cryptoCurrency.supportedDerivationPathTypes) {
final testPath = wallet.cryptoCurrency.constructDerivePath(
derivePathType: t,
chain: 0,
index: 0,
);
- if (voData is SparkViewOnlyWalletData) {
- type = t;
- } else if (testPath.startsWith((voData as ExtendedKeysViewOnlyWalletData).xPubs.first.path)) {
+ if (testPath.startsWith(voData.xPubs.first.path)) {
type = t;
break;
}
@@ -259,10 +257,7 @@ class _ReceiveViewState extends ConsumerState {
),
);
- final address = await wallet.generateNextSparkAddress();
- await ref.read(mainDBProvider).isar.writeTxn(() async {
- await ref.read(mainDBProvider).isar.addresses.put(address);
- });
+ final address = await wallet.generateNextSparkAddress(saveToDB: true);
shouldPop = true;
@@ -300,6 +295,32 @@ class _ReceiveViewState extends ConsumerState {
}
}
+ StreamSubscription _sub(AddressType type) {
+ return ref
+ .read(mainDBProvider)
+ .isar
+ .addresses
+ .where()
+ .walletIdEqualTo(walletId)
+ .filter()
+ .typeEqualTo(type)
+ .and()
+ .subTypeEqualTo(AddressSubType.receiving)
+ .sortByDerivationIndexDesc()
+ .findFirst()
+ .asStream()
+ .listen((event) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ setState(() {
+ _addressMap[type] =
+ event?.value ?? _addressMap[type] ?? "[No address yet]";
+ });
+ }
+ });
+ });
+ }
+
@override
void initState() {
walletId = widget.walletId;
@@ -314,6 +335,9 @@ class _ReceiveViewState extends ConsumerState {
if (wallet is ViewOnlyOptionInterface && wallet.isViewOnly) {
_showMultiType = false;
+ if (wallet.viewOnlyType == .spark) {
+ _walletAddressTypes.add(.spark);
+ }
} else {
_showMultiType =
_supportsSpark ||
@@ -323,7 +347,9 @@ class _ReceiveViewState extends ConsumerState