mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-19 10:41:02 +00:00
Merge branch 'staging' into tor
This commit is contained in:
commit
a48ec94927
31 changed files with 1263 additions and 357 deletions
|
@ -333,53 +333,57 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
Constants.size.circularBorderRadius,
|
Constants.size.circularBorderRadius,
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: Semantics(
|
||||||
autofocus: isDesktop,
|
label: "Search Text Field. Inputs Text To Search In Wallets.",
|
||||||
autocorrect: !isDesktop,
|
excludeSemantics: true,
|
||||||
enableSuggestions: !isDesktop,
|
child: TextField(
|
||||||
controller: _searchFieldController,
|
autofocus: isDesktop,
|
||||||
focusNode: _searchFocusNode,
|
autocorrect: !isDesktop,
|
||||||
onChanged: (value) => setState(() => _searchTerm = value),
|
enableSuggestions: !isDesktop,
|
||||||
style: STextStyles.field(context),
|
controller: _searchFieldController,
|
||||||
decoration: standardInputDecoration(
|
focusNode: _searchFocusNode,
|
||||||
"Search",
|
onChanged: (value) => setState(() => _searchTerm = value),
|
||||||
_searchFocusNode,
|
style: STextStyles.field(context),
|
||||||
context,
|
decoration: standardInputDecoration(
|
||||||
desktopMed: isDesktop,
|
"Search",
|
||||||
).copyWith(
|
_searchFocusNode,
|
||||||
prefixIcon: Padding(
|
context,
|
||||||
padding: const EdgeInsets.symmetric(
|
desktopMed: isDesktop,
|
||||||
horizontal: 10,
|
).copyWith(
|
||||||
vertical: 16,
|
prefixIcon: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.svg.search,
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: SvgPicture.asset(
|
suffixIcon: _searchFieldController.text.isNotEmpty
|
||||||
Assets.svg.search,
|
? Padding(
|
||||||
width: 16,
|
padding: const EdgeInsets.only(right: 0),
|
||||||
height: 16,
|
child: UnconstrainedBox(
|
||||||
),
|
child: Row(
|
||||||
),
|
children: [
|
||||||
suffixIcon: _searchFieldController.text.isNotEmpty
|
TextFieldIconButton(
|
||||||
? Padding(
|
child: const XIcon(),
|
||||||
padding: const EdgeInsets.only(right: 0),
|
onTap: () async {
|
||||||
child: UnconstrainedBox(
|
setState(() {
|
||||||
child: Row(
|
_searchFieldController.text = "";
|
||||||
children: [
|
_searchTerm = "";
|
||||||
TextFieldIconButton(
|
});
|
||||||
child: const XIcon(),
|
},
|
||||||
onTap: () async {
|
|
||||||
setState(() {
|
|
||||||
_searchFieldController.text = "";
|
|
||||||
_searchTerm = "";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
)
|
),
|
||||||
: null,
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
|
|
|
@ -238,14 +238,22 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
key: const Key("genRandomWalletNameButtonKey"),
|
key: const Key("genRandomWalletNameButtonKey"),
|
||||||
child: _showDiceIcon
|
child: _showDiceIcon
|
||||||
? DiceIcon(
|
? Semantics(
|
||||||
width: isDesktop ? 20 : 17,
|
label: "Generate Random Wallet Name Button. Generates A Random Name For Wallet.",
|
||||||
height: isDesktop ? 20 : 17,
|
excludeSemantics: true,
|
||||||
)
|
child: DiceIcon(
|
||||||
: XIcon(
|
width: isDesktop ? 20 : 17,
|
||||||
|
height: isDesktop ? 20 : 17,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Semantics(
|
||||||
|
label: "Generate Random Wallet Name Button. Generates A Random Name For Wallet.",
|
||||||
|
excludeSemantics: true,
|
||||||
|
child: XIcon(
|
||||||
width: isDesktop ? 21 : 18,
|
width: isDesktop ? 21 : 18,
|
||||||
height: isDesktop ? 21 : 18,
|
height: isDesktop ? 21 : 18,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (_showDiceIcon) {
|
if (_showDiceIcon) {
|
||||||
textEditingController.text =
|
textEditingController.text =
|
||||||
|
|
|
@ -140,6 +140,7 @@ class _NewWalletRecoveryPhraseViewState
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Copy Button. Copies The Recovery Phrase To Clipboard.",
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
.background,
|
.background,
|
||||||
|
|
|
@ -87,6 +87,7 @@ class _NewWalletRecoveryPhraseWarningViewState
|
||||||
right: 10,
|
right: 10,
|
||||||
),
|
),
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Question Button. Opens A Dialog For Recovery Phrase Explanation.",
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.circleQuestion,
|
Assets.svg.circleQuestion,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
|
|
@ -636,6 +636,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "View QR Code Button. Opens Camera To Scan QR Code For Restoring Wallet.",
|
||||||
key: const Key("restoreWalletViewQrCodeButton"),
|
key: const Key("restoreWalletViewQrCodeButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
@ -662,6 +663,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Paste Button. Pastes From Clipboard For Restoring Wallet.",
|
||||||
key: const Key("restoreWalletPasteButton"),
|
key: const Key("restoreWalletPasteButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
|
|
@ -781,31 +781,35 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
child: RoundedContainer(
|
child: Semantics(
|
||||||
padding: isDesktop
|
label: "Swap Button. Reverse The Exchange Currencies.",
|
||||||
? const EdgeInsets.all(6)
|
excludeSemantics: true,
|
||||||
: const EdgeInsets.all(2),
|
child: RoundedContainer(
|
||||||
color: Theme.of(context)
|
padding: isDesktop
|
||||||
.extension<StackColors>()!
|
? const EdgeInsets.all(6)
|
||||||
.buttonBackSecondary,
|
: const EdgeInsets.all(2),
|
||||||
radiusMultiplier: 0.75,
|
color: Theme.of(context)
|
||||||
child: GestureDetector(
|
.extension<StackColors>()!
|
||||||
onTap: () async {
|
.buttonBackSecondary,
|
||||||
await _swap();
|
radiusMultiplier: 0.75,
|
||||||
},
|
child: GestureDetector(
|
||||||
child: Padding(
|
onTap: () async {
|
||||||
padding: const EdgeInsets.all(4),
|
await _swap();
|
||||||
child: SvgPicture.asset(
|
},
|
||||||
Assets.svg.swap,
|
child: Padding(
|
||||||
width: 20,
|
padding: const EdgeInsets.all(4),
|
||||||
height: 20,
|
child: SvgPicture.asset(
|
||||||
color: Theme.of(context)
|
Assets.svg.swap,
|
||||||
.extension<StackColors>()!
|
width: 20,
|
||||||
.accentColorDark,
|
height: 20,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.accentColorDark,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -191,6 +191,7 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Notifications Button. Takes To Notifications Page.",
|
||||||
key: const Key("walletsViewAlertsButton"),
|
key: const Key("walletsViewAlertsButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
@ -254,6 +255,7 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Settings Button. Takes To Settings Page.",
|
||||||
key: const Key("walletsViewSettingsButton"),
|
key: const Key("walletsViewSettingsButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
|
|
@ -9,7 +9,6 @@ import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/biometrics.dart';
|
import 'package:stackwallet/utilities/biometrics.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
@ -139,6 +138,8 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
||||||
.background,
|
.background,
|
||||||
counterText: "",
|
counterText: "",
|
||||||
),
|
),
|
||||||
|
isRandom:
|
||||||
|
ref.read(prefsChangeNotifierProvider).randomizePIN,
|
||||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -221,6 +222,8 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
|
||||||
),
|
),
|
||||||
selectedFieldDecoration: _pinPutDecoration,
|
selectedFieldDecoration: _pinPutDecoration,
|
||||||
followingFieldDecoration: _pinPutDecoration,
|
followingFieldDecoration: _pinPutDecoration,
|
||||||
|
isRandom:
|
||||||
|
ref.read(prefsChangeNotifierProvider).randomizePIN,
|
||||||
onSubmit: (String pin) async {
|
onSubmit: (String pin) async {
|
||||||
// _onSubmitCount++;
|
// _onSubmitCount++;
|
||||||
// if (_onSubmitCount - _onSubmitFailCount > 1) return;
|
// if (_onSubmitCount - _onSubmitFailCount > 1) return;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
@ -21,8 +20,8 @@ import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/widgets/background.dart';
|
import 'package:stackwallet/widgets/background.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
|
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
|
||||||
import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart';
|
|
||||||
import 'package:stackwallet/widgets/shake/shake.dart';
|
import 'package:stackwallet/widgets/shake/shake.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
@ -39,6 +38,7 @@ class LockscreenView extends ConsumerStatefulWidget {
|
||||||
this.routeOnSuccessArguments,
|
this.routeOnSuccessArguments,
|
||||||
this.biometrics = const Biometrics(),
|
this.biometrics = const Biometrics(),
|
||||||
this.onSuccess,
|
this.onSuccess,
|
||||||
|
this.customKeyLabel = "Button",
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
static const String routeName = "/lockscreen";
|
static const String routeName = "/lockscreen";
|
||||||
|
@ -53,6 +53,8 @@ class LockscreenView extends ConsumerStatefulWidget {
|
||||||
final String biometricsCancelButtonString;
|
final String biometricsCancelButtonString;
|
||||||
final Biometrics biometrics;
|
final Biometrics biometrics;
|
||||||
final VoidCallback? onSuccess;
|
final VoidCallback? onSuccess;
|
||||||
|
final String customKeyLabel;
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<LockscreenView> createState() => _LockscreenViewState();
|
ConsumerState<LockscreenView> createState() => _LockscreenViewState();
|
||||||
|
@ -90,7 +92,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
|
|
||||||
final manager =
|
final manager =
|
||||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||||
if (manager.coin == Coin.monero || manager.coin == Coin.wownero) {
|
if (manager.coin == Coin.monero) {
|
||||||
await showLoading(
|
await showLoading(
|
||||||
opaqueBG: true,
|
opaqueBG: true,
|
||||||
whileFuture: manager.initializeExisting(),
|
whileFuture: manager.initializeExisting(),
|
||||||
|
@ -226,6 +228,26 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
// check prefs and hide if user has biometrics toggle off?
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 40.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
if (ref.read(prefsChangeNotifierProvider).useBiometrics ==
|
||||||
|
true)
|
||||||
|
CustomTextButton(
|
||||||
|
text: "Use biometrics",
|
||||||
|
onTap: () async {
|
||||||
|
await _checkUseBiometrics();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 55,
|
||||||
|
),
|
||||||
Shake(
|
Shake(
|
||||||
animationDuration: const Duration(milliseconds: 700),
|
animationDuration: const Duration(milliseconds: 700),
|
||||||
animationRange: 12,
|
animationRange: 12,
|
||||||
|
@ -244,12 +266,12 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
height: 52,
|
height: 52,
|
||||||
),
|
),
|
||||||
CustomPinPut(
|
CustomPinPut(
|
||||||
customKey: CustomKey(
|
// customKey: CustomKey(
|
||||||
onPressed: _checkUseBiometrics,
|
// onPressed: _checkUseBiometrics,
|
||||||
iconAssetName: Platform.isIOS
|
// iconAssetName: Platform.isIOS
|
||||||
? Assets.svg.faceId
|
// ? Assets.svg.faceId
|
||||||
: Assets.svg.fingerprint,
|
// : Assets.svg.fingerprint,
|
||||||
),
|
// ),
|
||||||
fieldsCount: Constants.pinLength,
|
fieldsCount: Constants.pinLength,
|
||||||
eachFieldHeight: 12,
|
eachFieldHeight: 12,
|
||||||
eachFieldWidth: 12,
|
eachFieldWidth: 12,
|
||||||
|
@ -285,6 +307,9 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
|
||||||
),
|
),
|
||||||
selectedFieldDecoration: _pinPutDecoration,
|
selectedFieldDecoration: _pinPutDecoration,
|
||||||
followingFieldDecoration: _pinPutDecoration,
|
followingFieldDecoration: _pinPutDecoration,
|
||||||
|
isRandom: ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.randomizePIN,
|
||||||
onSubmit: (String pin) async {
|
onSubmit: (String pin) async {
|
||||||
_attempts++;
|
_attempts++;
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Address List Pop-up Button. Opens A Pop-up For Adress List Button.",
|
||||||
key: const Key("walletNetworkSettingsAddNewNodeViewButton"),
|
key: const Key("walletNetworkSettingsAddNewNodeViewButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
|
|
@ -981,6 +981,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
children: [
|
children: [
|
||||||
_addressToggleFlag
|
_addressToggleFlag
|
||||||
? TextFieldIconButton(
|
? TextFieldIconButton(
|
||||||
|
semanticsLabel: "Clear Button. Clears The Address Field Input.",
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"sendViewClearAddressFieldButtonKey"),
|
"sendViewClearAddressFieldButtonKey"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -997,6 +998,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
child: const XIcon(),
|
child: const XIcon(),
|
||||||
)
|
)
|
||||||
: TextFieldIconButton(
|
: TextFieldIconButton(
|
||||||
|
semanticsLabel: "Paste Button. Pastes From Clipboard To Address Field Input.",
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"sendViewPasteAddressFieldButtonKey"),
|
"sendViewPasteAddressFieldButtonKey"),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
@ -1046,6 +1048,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
if (sendToController.text.isEmpty)
|
if (sendToController.text.isEmpty)
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
|
semanticsLabel: "Address Book Button. Opens Address Book For Address Field.",
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"sendViewAddressBookButtonKey"),
|
"sendViewAddressBookButtonKey"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -1058,6 +1061,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
||||||
),
|
),
|
||||||
if (sendToController.text.isEmpty)
|
if (sendToController.text.isEmpty)
|
||||||
TextFieldIconButton(
|
TextFieldIconButton(
|
||||||
|
semanticsLabel: "Scan QR Button. Opens Camera For Scanning QR Code.",
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"sendViewScanQrButtonKey"),
|
"sendViewScanQrButtonKey"),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
|
|
@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||||
import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart';
|
import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart';
|
||||||
|
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/constants.dart';
|
import 'package:stackwallet/utilities/constants.dart';
|
||||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
|
||||||
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
@ -123,6 +123,8 @@ class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
||||||
.background,
|
.background,
|
||||||
counterText: "",
|
counterText: "",
|
||||||
),
|
),
|
||||||
|
isRandom:
|
||||||
|
ref.read(prefsChangeNotifierProvider).randomizePIN,
|
||||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
@ -188,6 +190,8 @@ class _ChangePinViewState extends ConsumerState<ChangePinView> {
|
||||||
.background,
|
.background,
|
||||||
counterText: "",
|
counterText: "",
|
||||||
),
|
),
|
||||||
|
isRandom:
|
||||||
|
ref.read(prefsChangeNotifierProvider).randomizePIN,
|
||||||
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
submittedFieldDecoration: _pinPutDecoration.copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.extension<StackColors>()!
|
.extension<StackColors>()!
|
||||||
|
|
|
@ -146,6 +146,53 @@ class SecurityView extends StatelessWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
RoundedWhiteContainer(
|
||||||
|
child: Consumer(
|
||||||
|
builder: (_, ref, __) {
|
||||||
|
return RawMaterialButton(
|
||||||
|
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
Constants.size.circularBorderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: null,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Randomize PIN Pad",
|
||||||
|
style: STextStyles.titleBold12(context),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 40,
|
||||||
|
child: DraggableSwitchButton(
|
||||||
|
isOn: ref.watch(
|
||||||
|
prefsChangeNotifierProvider
|
||||||
|
.select((value) => value.randomizePIN),
|
||||||
|
),
|
||||||
|
onValueChanged: (newValue) {
|
||||||
|
ref
|
||||||
|
.read(prefsChangeNotifierProvider)
|
||||||
|
.randomizePIN = newValue;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -14,6 +14,8 @@ import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/dialogs/basic_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
import 'package:stackwallet/widgets/icon_widgets/eth_token_icon.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
|
@ -36,6 +38,36 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
||||||
|
|
||||||
late final CachedEthTokenBalance cachedBalance;
|
late final CachedEthTokenBalance cachedBalance;
|
||||||
|
|
||||||
|
Future<bool> _loadTokenWallet(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await ref.read(tokenServiceProvider)!.initialize();
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
await showDialog<void>(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => BasicDialog(
|
||||||
|
title: "Failed to load token data",
|
||||||
|
desktopHeight: double.infinity,
|
||||||
|
desktopWidth: 450,
|
||||||
|
rightButton: PrimaryButton(
|
||||||
|
label: "OK",
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
if (!isDesktop) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _onPressed() async {
|
void _onPressed() async {
|
||||||
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
ref.read(tokenServiceStateProvider.state).state = EthTokenWallet(
|
||||||
token: widget.token,
|
token: widget.token,
|
||||||
|
@ -49,13 +81,17 @@ class _MyTokenSelectItemState extends ConsumerState<MyTokenSelectItem> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await showLoading<void>(
|
final success = await showLoading<bool>(
|
||||||
whileFuture: ref.read(tokenServiceProvider)!.initialize(),
|
whileFuture: _loadTokenWallet(context, ref),
|
||||||
context: context,
|
context: context,
|
||||||
isDesktop: isDesktop,
|
isDesktop: isDesktop,
|
||||||
message: "Loading ${widget.token.name}",
|
message: "Loading ${widget.token.name}",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
await Navigator.of(context).pushNamed(
|
await Navigator.of(context).pushNamed(
|
||||||
isDesktop ? DesktopTokenView.routeName : TokenView.routeName,
|
isDesktop ? DesktopTokenView.routeName : TokenView.routeName,
|
||||||
|
|
|
@ -99,56 +99,60 @@ class _RefreshButtonState extends ConsumerState<WalletRefreshButton> {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: isDesktop ? 22 : 36,
|
height: isDesktop ? 22 : 36,
|
||||||
width: isDesktop ? 22 : 36,
|
width: isDesktop ? 22 : 36,
|
||||||
child: MaterialButton(
|
child: Semantics(
|
||||||
color: isDesktop
|
label: "Refresh Button. Refreshes The Values In Summary.",
|
||||||
? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
|
excludeSemantics: true,
|
||||||
: null,
|
child: MaterialButton(
|
||||||
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
color: isDesktop
|
||||||
onPressed: () {
|
? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
|
||||||
if (widget.tokenContractAddress == null) {
|
: null,
|
||||||
final managerProvider = ref
|
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
.read(walletsChangeNotifierProvider)
|
onPressed: () {
|
||||||
.getManagerProvider(widget.walletId);
|
if (widget.tokenContractAddress == null) {
|
||||||
final isRefreshing = ref.read(managerProvider).isRefreshing;
|
final managerProvider = ref
|
||||||
if (!isRefreshing) {
|
.read(walletsChangeNotifierProvider)
|
||||||
_spinController.repeat?.call();
|
.getManagerProvider(widget.walletId);
|
||||||
ref
|
final isRefreshing = ref.read(managerProvider).isRefreshing;
|
||||||
.read(managerProvider)
|
if (!isRefreshing) {
|
||||||
.refresh()
|
_spinController.repeat?.call();
|
||||||
.then((_) => _spinController.stop?.call());
|
ref
|
||||||
|
.read(managerProvider)
|
||||||
|
.refresh()
|
||||||
|
.then((_) => _spinController.stop?.call());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
|
||||||
|
ref.read(tokenServiceProvider)!.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
if (!ref.read(tokenServiceProvider)!.isRefreshing) {
|
elevation: 0,
|
||||||
ref.read(tokenServiceProvider)!.refresh();
|
highlightElevation: 0,
|
||||||
}
|
hoverElevation: 0,
|
||||||
}
|
padding: EdgeInsets.zero,
|
||||||
},
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
elevation: 0,
|
shape: RoundedRectangleBorder(
|
||||||
highlightElevation: 0,
|
borderRadius: BorderRadius.circular(
|
||||||
hoverElevation: 0,
|
Constants.size.circularBorderRadius,
|
||||||
padding: EdgeInsets.zero,
|
),
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
),
|
||||||
shape: RoundedRectangleBorder(
|
child: RotatingArrows(
|
||||||
borderRadius: BorderRadius.circular(
|
spinByDefault: widget.initialSyncStatus == WalletSyncStatus.syncing,
|
||||||
Constants.size.circularBorderRadius,
|
width: isDesktop ? 12 : 24,
|
||||||
|
height: isDesktop ? 12 : 24,
|
||||||
|
controller: _spinController,
|
||||||
|
color: widget.overrideIconColor != null
|
||||||
|
? widget.overrideIconColor!
|
||||||
|
: isDesktop
|
||||||
|
? Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFieldDefaultSearchIconRight
|
||||||
|
: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.textFavoriteCard,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: RotatingArrows(
|
)
|
||||||
spinByDefault: widget.initialSyncStatus == WalletSyncStatus.syncing,
|
|
||||||
width: isDesktop ? 12 : 24,
|
|
||||||
height: isDesktop ? 12 : 24,
|
|
||||||
controller: _spinController,
|
|
||||||
color: widget.overrideIconColor != null
|
|
||||||
? widget.overrideIconColor!
|
|
||||||
: isDesktop
|
|
||||||
? Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textFieldDefaultSearchIconRight
|
|
||||||
: Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.textFavoriteCard,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart';
|
import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart';
|
||||||
|
@ -420,6 +421,33 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
eventBus: null,
|
eventBus: null,
|
||||||
textColor:
|
textColor:
|
||||||
Theme.of(context).extension<StackColors>()!.textDark,
|
Theme.of(context).extension<StackColors>()!.textDark,
|
||||||
|
actionButton: SecondaryButton(
|
||||||
|
label: "Cancel",
|
||||||
|
onPressed: () async {
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => StackDialog(
|
||||||
|
title: "Warning!",
|
||||||
|
message: "Skipping this process can completely"
|
||||||
|
" break your wallet. It is only meant to be done in"
|
||||||
|
" emergency situations where the migration fails"
|
||||||
|
" and will not let you continue. Still skip?",
|
||||||
|
leftButton: SecondaryButton(
|
||||||
|
label: "Cancel",
|
||||||
|
onPressed:
|
||||||
|
Navigator.of(context, rootNavigator: true).pop,
|
||||||
|
),
|
||||||
|
rightButton: SecondaryButton(
|
||||||
|
label: "Ok",
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
setState(() => _rescanningOnOpen = false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -473,6 +501,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Network Button. Takes To Network Status Page.",
|
||||||
key: const Key("walletViewRadioButton"),
|
key: const Key("walletViewRadioButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
@ -502,6 +531,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Notifications Button. Takes To Notifications Page.",
|
||||||
key: const Key("walletViewAlertsButton"),
|
key: const Key("walletViewAlertsButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
@ -569,6 +599,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
|
semanticsLabel: "Settings Button. Takes To Wallet Settings Page.",
|
||||||
key: const Key("walletViewSettingsButton"),
|
key: const Key("walletViewSettingsButton"),
|
||||||
size: 36,
|
size: 36,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
|
@ -790,7 +821,6 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
||||||
label: "Receive",
|
label: "Receive",
|
||||||
icon: const ReceiveNavIcon(),
|
icon: const ReceiveNavIcon(),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final coin = ref.read(managerProvider).coin;
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
unawaited(
|
unawaited(
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
|
|
|
@ -31,7 +31,11 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||||
import 'package:stackwallet/widgets/hover_text_field.dart';
|
import 'package:stackwallet/widgets/hover_text_field.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
|
|
||||||
|
@ -150,6 +154,83 @@ class _DesktopWalletViewState extends ConsumerState<DesktopWalletView> {
|
||||||
subMessage: "This only needs to run once per wallet",
|
subMessage: "This only needs to run once per wallet",
|
||||||
eventBus: null,
|
eventBus: null,
|
||||||
textColor: Theme.of(context).extension<StackColors>()!.textDark,
|
textColor: Theme.of(context).extension<StackColors>()!.textDark,
|
||||||
|
actionButton: SecondaryButton(
|
||||||
|
label: "Skip",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: () async {
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DesktopDialog(
|
||||||
|
maxWidth: 500,
|
||||||
|
maxHeight: double.infinity,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 32),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Warning!",
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const DesktopDialogCloseButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Text(
|
||||||
|
"Skipping this process can completely"
|
||||||
|
" break your wallet. It is only meant to be done in"
|
||||||
|
" emergency situations where the migration fails"
|
||||||
|
" and will not let you continue. Still skip?",
|
||||||
|
style: STextStyles.desktopTextSmall(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: SecondaryButton(
|
||||||
|
label: "Cancel",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: Navigator.of(context,
|
||||||
|
rootNavigator: true)
|
||||||
|
.pop,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: PrimaryButton(
|
||||||
|
label: "Ok",
|
||||||
|
buttonHeight: ButtonHeight.l,
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context,
|
||||||
|
rootNavigator: true)
|
||||||
|
.pop();
|
||||||
|
setState(
|
||||||
|
() => _rescanningOnOpen = false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
@ -1949,8 +1949,6 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
|
|
||||||
List<Map<String, dynamic>> allTransactions = [];
|
List<Map<String, dynamic>> allTransactions = [];
|
||||||
|
|
||||||
final currentHeight = await chainHeight;
|
|
||||||
|
|
||||||
for (final txHash in allTxHashes) {
|
for (final txHash in allTxHashes) {
|
||||||
final storedTx = await db
|
final storedTx = await db
|
||||||
.getTransactions(walletId)
|
.getTransactions(walletId)
|
||||||
|
@ -1958,7 +1956,9 @@ class BitcoinCashWallet extends CoinServiceAPI
|
||||||
.txidEqualTo(txHash["tx_hash"] as String)
|
.txidEqualTo(txHash["tx_hash"] as String)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|
||||||
if (storedTx == null || storedTx.address.value == null
|
if (storedTx == null ||
|
||||||
|
storedTx.address.value == null ||
|
||||||
|
storedTx.height == null
|
||||||
// zero conf messes this up
|
// zero conf messes this up
|
||||||
// !storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)
|
// !storedTx.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -253,13 +253,17 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
|
||||||
);
|
);
|
||||||
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
_credentials = web3dart.EthPrivateKey.fromHex(privateKey);
|
||||||
|
|
||||||
_deployedContract = web3dart.DeployedContract(
|
try {
|
||||||
ContractAbiExtensions.fromJsonList(
|
_deployedContract = web3dart.DeployedContract(
|
||||||
jsonList: tokenContract.abi!,
|
ContractAbiExtensions.fromJsonList(
|
||||||
name: tokenContract.name,
|
jsonList: tokenContract.abi!,
|
||||||
),
|
name: tokenContract.name,
|
||||||
contractAddress,
|
),
|
||||||
);
|
contractAddress,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_sendFunction = _deployedContract.function('transfer');
|
_sendFunction = _deployedContract.function('transfer');
|
||||||
|
@ -328,13 +332,17 @@ class EthTokenWallet extends ChangeNotifier with EthTokenCache {
|
||||||
//====================================================================
|
//====================================================================
|
||||||
}
|
}
|
||||||
|
|
||||||
_deployedContract = web3dart.DeployedContract(
|
try {
|
||||||
ContractAbiExtensions.fromJsonList(
|
_deployedContract = web3dart.DeployedContract(
|
||||||
jsonList: tokenContract.abi!,
|
ContractAbiExtensions.fromJsonList(
|
||||||
name: tokenContract.name,
|
jsonList: tokenContract.abi!,
|
||||||
),
|
name: tokenContract.name,
|
||||||
contractAddress,
|
),
|
||||||
);
|
contractAddress,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
_sendFunction = _deployedContract.function('transfer');
|
_sendFunction = _deployedContract.function('transfer');
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
import 'package:web3dart/web3dart.dart';
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
extension ContractAbiExtensions on ContractAbi {
|
extension ContractAbiExtensions on ContractAbi {
|
||||||
|
@ -7,50 +8,61 @@ extension ContractAbiExtensions on ContractAbi {
|
||||||
required String name,
|
required String name,
|
||||||
required String jsonList,
|
required String jsonList,
|
||||||
}) {
|
}) {
|
||||||
final List<ContractFunction> functions = [];
|
try {
|
||||||
final List<ContractEvent> events = [];
|
final List<ContractFunction> functions = [];
|
||||||
|
final List<ContractEvent> events = [];
|
||||||
|
|
||||||
final list = List<Map<String, dynamic>>.from(jsonDecode(jsonList) as List);
|
final list =
|
||||||
|
List<Map<String, dynamic>>.from(jsonDecode(jsonList) as List);
|
||||||
|
|
||||||
for (final json in list) {
|
for (final json in list) {
|
||||||
final type = json["type"] as String;
|
final type = json["type"] as String;
|
||||||
final name = json["name"] as String? ?? "";
|
final name = json["name"] as String? ?? "";
|
||||||
|
|
||||||
if (type == "event") {
|
if (type == "event") {
|
||||||
final anonymous = json["anonymous"] as bool? ?? false;
|
final anonymous = json["anonymous"] as bool? ?? false;
|
||||||
final List<EventComponent<dynamic>> components = [];
|
final List<EventComponent<dynamic>> components = [];
|
||||||
|
|
||||||
for (final input in json["inputs"] as List) {
|
if (json["inputs"] is List) {
|
||||||
components.add(
|
for (final input in json["inputs"] as List) {
|
||||||
EventComponent(
|
components.add(
|
||||||
_parseParam(input as Map),
|
EventComponent(
|
||||||
input['indexed'] as bool? ?? false,
|
_parseParam(input as Map),
|
||||||
),
|
input['indexed'] as bool? ?? false,
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
events.add(ContractEvent(anonymous, name, components));
|
events.add(ContractEvent(anonymous, name, components));
|
||||||
} else {
|
} else {
|
||||||
final mutability = _mutabilityNames[json['stateMutability']];
|
final mutability = _mutabilityNames[json['stateMutability']];
|
||||||
final parsedType = _functionTypeNames[json['type']];
|
final parsedType = _functionTypeNames[json['type']];
|
||||||
if (parsedType != null) {
|
if (parsedType != null) {
|
||||||
final inputs = _parseParams(json['inputs'] as List?);
|
final inputs = _parseParams(json['inputs'] as List?);
|
||||||
final outputs = _parseParams(json['outputs'] as List?);
|
final outputs = _parseParams(json['outputs'] as List?);
|
||||||
|
|
||||||
functions.add(
|
functions.add(
|
||||||
ContractFunction(
|
ContractFunction(
|
||||||
name,
|
name,
|
||||||
inputs,
|
inputs,
|
||||||
outputs: outputs,
|
outputs: outputs,
|
||||||
type: parsedType,
|
type: parsedType,
|
||||||
mutability: mutability ?? StateMutability.nonPayable,
|
mutability: mutability ?? StateMutability.nonPayable,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ContractAbi(name, functions, events);
|
return ContractAbi(name, functions, events);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Failed to parse ABI for $name: $e\n$s",
|
||||||
|
level: LogLevel.Error,
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Map<String, ContractFunctionType> _functionTypeNames = {
|
static const Map<String, ContractFunctionType> _functionTypeNames = {
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Prefs extends ChangeNotifier {
|
||||||
if (!_initialized) {
|
if (!_initialized) {
|
||||||
_currency = await _getPreferredCurrency();
|
_currency = await _getPreferredCurrency();
|
||||||
// _exchangeRateType = await _getExchangeRateType();
|
// _exchangeRateType = await _getExchangeRateType();
|
||||||
|
_randomizePIN = await _getRandomizePIN();
|
||||||
_useBiometrics = await _getUseBiometrics();
|
_useBiometrics = await _getUseBiometrics();
|
||||||
_hasPin = await _getHasPin();
|
_hasPin = await _getHasPin();
|
||||||
_language = await _getPreferredLanguage();
|
_language = await _getPreferredLanguage();
|
||||||
|
@ -295,6 +296,27 @@ class Prefs extends ChangeNotifier {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// randomize PIN
|
||||||
|
|
||||||
|
bool _randomizePIN = false;
|
||||||
|
|
||||||
|
bool get randomizePIN => _randomizePIN;
|
||||||
|
|
||||||
|
set randomizePIN(bool randomizePIN) {
|
||||||
|
if (_randomizePIN != randomizePIN) {
|
||||||
|
DB.instance.put<dynamic>(
|
||||||
|
boxName: DB.boxNamePrefs, key: "randomizePIN", value: randomizePIN);
|
||||||
|
_randomizePIN = randomizePIN;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _getRandomizePIN() async {
|
||||||
|
return await DB.instance.get<dynamic>(
|
||||||
|
boxName: DB.boxNamePrefs, key: "randomizePIN") as bool? ??
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
// use biometrics
|
// use biometrics
|
||||||
|
|
||||||
bool _useBiometrics = false;
|
bool _useBiometrics = false;
|
||||||
|
|
|
@ -13,6 +13,7 @@ class AppBarIconButton extends StatelessWidget {
|
||||||
// this.circularBorderRadius = 10.0,
|
// this.circularBorderRadius = 10.0,
|
||||||
this.size = 36.0,
|
this.size = 36.0,
|
||||||
this.shadows = const [],
|
this.shadows = const [],
|
||||||
|
this.semanticsLabel = "Button",
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget icon;
|
final Widget icon;
|
||||||
|
@ -21,6 +22,7 @@ class AppBarIconButton extends StatelessWidget {
|
||||||
// final double circularBorderRadius;
|
// final double circularBorderRadius;
|
||||||
final double size;
|
final double size;
|
||||||
final List<BoxShadow> shadows;
|
final List<BoxShadow> shadows;
|
||||||
|
final String semanticsLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -32,16 +34,20 @@ class AppBarIconButton extends StatelessWidget {
|
||||||
color: color ?? Theme.of(context).extension<StackColors>()!.background,
|
color: color ?? Theme.of(context).extension<StackColors>()!.background,
|
||||||
boxShadow: shadows,
|
boxShadow: shadows,
|
||||||
),
|
),
|
||||||
child: MaterialButton(
|
child: Semantics(
|
||||||
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
excludeSemantics: true,
|
||||||
padding: EdgeInsets.zero,
|
label: semanticsLabel,
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
child: MaterialButton(
|
||||||
shape: RoundedRectangleBorder(
|
splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
borderRadius: BorderRadius.circular(1000),
|
padding: EdgeInsets.zero,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(1000),
|
||||||
|
),
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: icon,
|
||||||
),
|
),
|
||||||
onPressed: onPressed,
|
)
|
||||||
child: icon,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,12 +59,14 @@ class AppBarBackButton extends StatelessWidget {
|
||||||
this.isCompact = false,
|
this.isCompact = false,
|
||||||
this.size,
|
this.size,
|
||||||
this.iconSize,
|
this.iconSize,
|
||||||
|
this.semanticsLabel = "Back Button. Takes Back To Previous Page.",
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final bool isCompact;
|
final bool isCompact;
|
||||||
final double? size;
|
final double? size;
|
||||||
final double? iconSize;
|
final double? iconSize;
|
||||||
|
final String semanticsLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -71,24 +79,25 @@ class AppBarBackButton extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: const EdgeInsets.all(10),
|
: const EdgeInsets.all(10),
|
||||||
child: AppBarIconButton(
|
child: AppBarIconButton(
|
||||||
size: size ??
|
semanticsLabel: semanticsLabel,
|
||||||
(isDesktop
|
size: size ??
|
||||||
? isCompact
|
(isDesktop
|
||||||
? 42
|
? isCompact
|
||||||
: 56
|
? 42
|
||||||
: 32),
|
: 56
|
||||||
color: isDesktop
|
: 32),
|
||||||
? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
|
color: isDesktop
|
||||||
: Theme.of(context).extension<StackColors>()!.background,
|
? Theme.of(context).extension<StackColors>()!.textFieldDefaultBG
|
||||||
shadows: const [],
|
: Theme.of(context).extension<StackColors>()!.background,
|
||||||
icon: SvgPicture.asset(
|
shadows: const [],
|
||||||
Assets.svg.arrowLeft,
|
icon: SvgPicture.asset(
|
||||||
width: iconSize ?? (isCompact ? 18 : 24),
|
Assets.svg.arrowLeft,
|
||||||
height: iconSize ?? (isCompact ? 18 : 24),
|
width: iconSize ?? (isCompact ? 18 : 24),
|
||||||
color: Theme.of(context).extension<StackColors>()!.topNavIconPrimary,
|
height: iconSize ?? (isCompact ? 18 : 24),
|
||||||
),
|
color: Theme.of(context).extension<StackColors>()!.topNavIconPrimary,
|
||||||
onPressed: onPressed ?? Navigator.of(context).pop,
|
),
|
||||||
),
|
onPressed: onPressed ?? Navigator.of(context).pop,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||||
|
|
||||||
class CustomLoadingOverlay extends ConsumerStatefulWidget {
|
class CustomLoadingOverlay extends ConsumerStatefulWidget {
|
||||||
|
@ -14,12 +16,14 @@ class CustomLoadingOverlay extends ConsumerStatefulWidget {
|
||||||
this.subMessage,
|
this.subMessage,
|
||||||
required this.eventBus,
|
required this.eventBus,
|
||||||
this.textColor,
|
this.textColor,
|
||||||
|
this.actionButton,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String message;
|
final String message;
|
||||||
final String? subMessage;
|
final String? subMessage;
|
||||||
final EventBus? eventBus;
|
final EventBus? eventBus;
|
||||||
final Color? textColor;
|
final Color? textColor;
|
||||||
|
final Widget? actionButton;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<CustomLoadingOverlay> createState() =>
|
ConsumerState<CustomLoadingOverlay> createState() =>
|
||||||
|
@ -30,6 +34,7 @@ class _CustomLoadingOverlayState extends ConsumerState<CustomLoadingOverlay> {
|
||||||
double _percent = 0;
|
double _percent = 0;
|
||||||
|
|
||||||
late final StreamSubscription<double>? subscription;
|
late final StreamSubscription<double>? subscription;
|
||||||
|
final bool isDesktop = Util.isDesktop;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -49,68 +54,97 @@ class _CustomLoadingOverlayState extends ConsumerState<CustomLoadingOverlay> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Material(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
color: Colors.transparent,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: ConditionalParent(
|
||||||
children: [
|
condition: widget.actionButton != null,
|
||||||
Material(
|
builder: (child) => Stack(
|
||||||
color: Colors.transparent,
|
children: [
|
||||||
child: Center(
|
child,
|
||||||
child: Column(
|
if (isDesktop)
|
||||||
children: [
|
Row(
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
widget.message,
|
children: [
|
||||||
textAlign: TextAlign.center,
|
Padding(
|
||||||
style: STextStyles.pageTitleH2(context).copyWith(
|
padding: const EdgeInsets.all(16),
|
||||||
color: widget.textColor ??
|
child: SizedBox(
|
||||||
Theme.of(context)
|
width: 100,
|
||||||
.extension<StackColors>()!
|
child: widget.actionButton!,
|
||||||
.loadingOverlayTextColor,
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (!isDesktop)
|
||||||
|
Positioned(
|
||||||
|
bottom: 1,
|
||||||
|
left: 0,
|
||||||
|
right: 1,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: widget.actionButton!),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.eventBus != null)
|
),
|
||||||
const SizedBox(
|
],
|
||||||
height: 10,
|
),
|
||||||
),
|
child: Column(
|
||||||
if (widget.eventBus != null)
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
"${(_percent * 100).toStringAsFixed(2)}%",
|
children: [
|
||||||
style: STextStyles.pageTitleH2(context).copyWith(
|
Text(
|
||||||
color: widget.textColor ??
|
widget.message,
|
||||||
Theme.of(context)
|
textAlign: TextAlign.center,
|
||||||
.extension<StackColors>()!
|
style: STextStyles.pageTitleH2(context).copyWith(
|
||||||
.loadingOverlayTextColor,
|
color: widget.textColor ??
|
||||||
),
|
Theme.of(context)
|
||||||
),
|
.extension<StackColors>()!
|
||||||
if (widget.subMessage != null)
|
.loadingOverlayTextColor,
|
||||||
const SizedBox(
|
),
|
||||||
height: 10,
|
|
||||||
),
|
|
||||||
if (widget.subMessage != null)
|
|
||||||
Text(
|
|
||||||
widget.subMessage!,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: STextStyles.pageTitleH2(context).copyWith(
|
|
||||||
fontSize: 14,
|
|
||||||
color: widget.textColor ??
|
|
||||||
Theme.of(context)
|
|
||||||
.extension<StackColors>()!
|
|
||||||
.loadingOverlayTextColor,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
if (widget.eventBus != null)
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
if (widget.eventBus != null)
|
||||||
|
Text(
|
||||||
|
"${(_percent * 100).toStringAsFixed(2)}%",
|
||||||
|
style: STextStyles.pageTitleH2(context).copyWith(
|
||||||
|
color: widget.textColor ??
|
||||||
|
Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.loadingOverlayTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.subMessage != null)
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
if (widget.subMessage != null)
|
||||||
|
Text(
|
||||||
|
widget.subMessage!,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: STextStyles.pageTitleH2(context).copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
color: widget.textColor ??
|
||||||
|
Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.loadingOverlayTextColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 64,
|
||||||
|
),
|
||||||
|
const Center(
|
||||||
|
child: LoadingIndicator(
|
||||||
|
width: 100,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
),
|
||||||
height: 64,
|
|
||||||
),
|
|
||||||
const Center(
|
|
||||||
child: LoadingIndicator(
|
|
||||||
width: 100,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ class CustomPinPut extends StatefulWidget {
|
||||||
const CustomPinPut({
|
const CustomPinPut({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.fieldsCount,
|
required this.fieldsCount,
|
||||||
|
required this.isRandom,
|
||||||
this.height,
|
this.height,
|
||||||
this.width,
|
this.width,
|
||||||
this.onSubmit,
|
this.onSubmit,
|
||||||
|
@ -60,6 +61,8 @@ class CustomPinPut extends StatefulWidget {
|
||||||
|
|
||||||
final CustomKey? customKey;
|
final CustomKey? customKey;
|
||||||
|
|
||||||
|
final bool isRandom;
|
||||||
|
|
||||||
/// Displayed fields count. PIN code length.
|
/// Displayed fields count. PIN code length.
|
||||||
final int fieldsCount;
|
final int fieldsCount;
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ class CustomPinPutState extends State<CustomPinPut>
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_textControllerValue = ValueNotifier(_controller.value.text);
|
_textControllerValue = ValueNotifier(_controller.value.text);
|
||||||
}
|
}
|
||||||
if (pin.length == widget.fieldsCount) {
|
// if (pin.length == widget.fieldsCount) {
|
||||||
widget.onSubmit?.call(pin);
|
// widget.onSubmit?.call(pin);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,9 @@ class CustomPinPutState extends State<CustomPinPut>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// final bool randomize = ref
|
||||||
|
// .read(prefsChangeNotifierProvider)
|
||||||
|
// .randomizePIN;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
height: widget.height,
|
height: widget.height,
|
||||||
|
@ -69,6 +72,7 @@ class CustomPinPutState extends State<CustomPinPut>
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: PinKeyboard(
|
child: PinKeyboard(
|
||||||
|
isRandom: widget.isRandom,
|
||||||
customKey: widget.customKey,
|
customKey: widget.customKey,
|
||||||
onNumberKeyPressed: (number) {
|
onNumberKeyPressed: (number) {
|
||||||
if (_controller.text.length < widget.fieldsCount) {
|
if (_controller.text.length < widget.fieldsCount) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:stackwallet/utilities/assets.dart';
|
import 'package:stackwallet/utilities/assets.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
@ -140,15 +141,19 @@ class _BackspaceKeyState extends State<BackspaceKey> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Center(
|
child: Semantics(
|
||||||
child: SvgPicture.asset(
|
label: "Backspace Button. Deletes The Last Digit.",
|
||||||
Assets.svg.delete,
|
excludeSemantics: true,
|
||||||
width: 20,
|
child: Center(
|
||||||
height: 20,
|
child: SvgPicture.asset(
|
||||||
color:
|
Assets.svg.delete,
|
||||||
Theme.of(context).extension<StackColors>()!.numpadTextDefault,
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
color:
|
||||||
|
Theme.of(context).extension<StackColors>()!.numpadTextDefault,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -198,10 +203,12 @@ class CustomKey extends StatelessWidget {
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
this.iconAssetName,
|
this.iconAssetName,
|
||||||
|
this.semanticsLabel = "Button",
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
final String? iconAssetName;
|
final String? iconAssetName;
|
||||||
|
final String semanticsLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -213,32 +220,197 @@ class CustomKey extends StatelessWidget {
|
||||||
color: Theme.of(context).extension<StackColors>()!.numpadBackDefault,
|
color: Theme.of(context).extension<StackColors>()!.numpadBackDefault,
|
||||||
shadows: const [],
|
shadows: const [],
|
||||||
),
|
),
|
||||||
child: MaterialButton(
|
child: Semantics(
|
||||||
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
label: semanticsLabel,
|
||||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
excludeSemantics: true,
|
||||||
shape: const StadiumBorder(),
|
child: MaterialButton(
|
||||||
onPressed: () {
|
// splashColor: Theme.of(context).extension<StackColors>()!.highlight,
|
||||||
onPressed.call();
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
},
|
shape: const StadiumBorder(),
|
||||||
child: Center(
|
onPressed: () {
|
||||||
child: iconAssetName == null
|
onPressed.call();
|
||||||
? null
|
},
|
||||||
: SvgPicture.asset(
|
child: Center(
|
||||||
iconAssetName!,
|
child: iconAssetName == null
|
||||||
width: 20,
|
? null
|
||||||
height: 20,
|
: SvgPicture.asset(
|
||||||
color: Theme.of(context)
|
iconAssetName!,
|
||||||
.extension<StackColors>()!
|
width: 20,
|
||||||
.numpadTextDefault,
|
height: 20,
|
||||||
),
|
color: Theme.of(context)
|
||||||
|
.extension<StackColors>()!
|
||||||
|
.numpadTextDefault,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PinKeyboard extends ConsumerWidget {
|
||||||
|
const PinKeyboard({
|
||||||
|
Key? key,
|
||||||
|
required this.onNumberKeyPressed,
|
||||||
|
required this.onBackPressed,
|
||||||
|
required this.onSubmitPressed,
|
||||||
|
required this.isRandom,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.width = 264,
|
||||||
|
this.height = 360,
|
||||||
|
this.customKey,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final ValueSetter<String> onNumberKeyPressed;
|
||||||
|
final VoidCallback onBackPressed;
|
||||||
|
final VoidCallback onSubmitPressed;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final double? width;
|
||||||
|
final double? height;
|
||||||
|
final CustomKey? customKey;
|
||||||
|
final bool isRandom;
|
||||||
|
|
||||||
|
void _backHandler() {
|
||||||
|
onBackPressed.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _submitHandler() {
|
||||||
|
onSubmitPressed.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _numberHandler(String number) {
|
||||||
|
onNumberKeyPressed.call(number);
|
||||||
|
HapticFeedback.lightImpact();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final list = [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
|
"8",
|
||||||
|
"9",
|
||||||
|
"0",
|
||||||
|
];
|
||||||
|
|
||||||
|
// final isRandom = ref.read(prefsChangeNotifierProvider).randomizePIN;
|
||||||
|
|
||||||
|
if (isRandom) list.shuffle();
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
color: backgroundColor ?? Colors.transparent,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
NumberKey(
|
||||||
|
number: list[0],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[1],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[2],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
NumberKey(
|
||||||
|
number: list[3],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[4],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[5],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
NumberKey(
|
||||||
|
number: list[6],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[7],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[8],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
BackspaceKey(
|
||||||
|
onPressed: _backHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[9],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
SubmitKey(
|
||||||
|
onPressed: _submitHandler,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PinKeyboard extends StatelessWidget {
|
class RandomKeyboard extends StatelessWidget {
|
||||||
const PinKeyboard({
|
const RandomKeyboard({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.onNumberKeyPressed,
|
required this.onNumberKeyPressed,
|
||||||
required this.onBackPressed,
|
required this.onBackPressed,
|
||||||
|
@ -268,10 +440,24 @@ class PinKeyboard extends StatelessWidget {
|
||||||
void _numberHandler(String number) {
|
void _numberHandler(String number) {
|
||||||
onNumberKeyPressed.call(number);
|
onNumberKeyPressed.call(number);
|
||||||
HapticFeedback.lightImpact();
|
HapticFeedback.lightImpact();
|
||||||
|
debugPrint("NUMBER: $number");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final list = [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
|
"8",
|
||||||
|
"9",
|
||||||
|
"0",
|
||||||
|
];
|
||||||
|
list.shuffle();
|
||||||
return Container(
|
return Container(
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
@ -281,21 +467,21 @@ class PinKeyboard extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "1",
|
number: list[0],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
),
|
),
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "2",
|
number: list[1],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
),
|
),
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "3",
|
number: list[2],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -306,21 +492,21 @@ class PinKeyboard extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "4",
|
number: list[3],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
),
|
),
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "5",
|
number: list[4],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
),
|
),
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "6",
|
number: list[5],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -331,21 +517,21 @@ class PinKeyboard extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "7",
|
number: list[6],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
),
|
),
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "8",
|
number: list[7],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
),
|
),
|
||||||
NumberKey(
|
NumberKey(
|
||||||
number: "9",
|
number: list[8],
|
||||||
onPressed: _numberHandler,
|
onPressed: _numberHandler,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -355,28 +541,22 @@ class PinKeyboard extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
customKey == null
|
|
||||||
? const SizedBox(
|
|
||||||
height: 72,
|
|
||||||
width: 72,
|
|
||||||
)
|
|
||||||
: customKey!,
|
|
||||||
const SizedBox(
|
|
||||||
width: 24,
|
|
||||||
),
|
|
||||||
NumberKey(
|
|
||||||
number: "0",
|
|
||||||
onPressed: _numberHandler,
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
width: 24,
|
|
||||||
),
|
|
||||||
BackspaceKey(
|
BackspaceKey(
|
||||||
onPressed: _backHandler,
|
onPressed: _backHandler,
|
||||||
),
|
),
|
||||||
// SubmitKey(
|
const SizedBox(
|
||||||
// onPressed: _submitHandler,
|
width: 24,
|
||||||
// ),
|
),
|
||||||
|
NumberKey(
|
||||||
|
number: list[9],
|
||||||
|
onPressed: _numberHandler,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
SubmitKey(
|
||||||
|
onPressed: _submitHandler,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
104
lib/widgets/dialogs/basic_dialog.dart
Normal file
104
lib/widgets/dialogs/basic_dialog.dart
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||||
|
|
||||||
|
class BasicDialog extends StatelessWidget {
|
||||||
|
const BasicDialog({
|
||||||
|
Key? key,
|
||||||
|
this.leftButton,
|
||||||
|
this.rightButton,
|
||||||
|
this.icon,
|
||||||
|
required this.title,
|
||||||
|
this.message,
|
||||||
|
this.desktopHeight = 474,
|
||||||
|
this.desktopWidth = 641,
|
||||||
|
this.canPopWithBackButton = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget? leftButton;
|
||||||
|
final Widget? rightButton;
|
||||||
|
|
||||||
|
final Widget? icon;
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String? message;
|
||||||
|
|
||||||
|
final double? desktopHeight;
|
||||||
|
final double desktopWidth;
|
||||||
|
|
||||||
|
final bool canPopWithBackButton;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isDesktop = Util.isDesktop;
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
return DesktopDialog(
|
||||||
|
maxHeight: desktopHeight,
|
||||||
|
maxWidth: desktopWidth,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 32),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: STextStyles.desktopH3(context),
|
||||||
|
),
|
||||||
|
const DesktopDialogCloseButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (message != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Text(
|
||||||
|
message!,
|
||||||
|
style: STextStyles.desktopTextSmall(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (leftButton != null || rightButton != null)
|
||||||
|
const SizedBox(
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
if (leftButton != null || rightButton != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
leftButton != null
|
||||||
|
? Expanded(child: leftButton!)
|
||||||
|
: const Spacer(),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
rightButton != null
|
||||||
|
? Expanded(child: rightButton!)
|
||||||
|
: const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async {
|
||||||
|
return canPopWithBackButton;
|
||||||
|
},
|
||||||
|
child: StackDialog(
|
||||||
|
title: title,
|
||||||
|
leftButton: leftButton,
|
||||||
|
rightButton: rightButton,
|
||||||
|
icon: icon,
|
||||||
|
message: message,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ class TextFieldIconButton extends StatefulWidget {
|
||||||
this.onTap,
|
this.onTap,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.color = Colors.transparent,
|
this.color = Colors.transparent,
|
||||||
|
this.semanticsLabel = "Button",
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final double width;
|
final double width;
|
||||||
|
@ -15,6 +16,7 @@ class TextFieldIconButton extends StatefulWidget {
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Color color;
|
final Color color;
|
||||||
|
final String semanticsLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TextFieldIconButton> createState() => _TextFieldIconButtonState();
|
State<TextFieldIconButton> createState() => _TextFieldIconButtonState();
|
||||||
|
@ -36,21 +38,25 @@ class _TextFieldIconButtonState extends State<TextFieldIconButton> {
|
||||||
width: widget.width,
|
width: widget.width,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(100),
|
borderRadius: BorderRadius.circular(100),
|
||||||
child: RawMaterialButton(
|
child: Semantics(
|
||||||
constraints: BoxConstraints(
|
label: widget.semanticsLabel,
|
||||||
minWidth: widget.width,
|
excludeSemantics: true,
|
||||||
minHeight: widget.height,
|
child: RawMaterialButton(
|
||||||
),
|
constraints: BoxConstraints(
|
||||||
onPressed: onTap,
|
minWidth: widget.width,
|
||||||
child: Container(
|
minHeight: widget.height,
|
||||||
width: widget.width,
|
),
|
||||||
height: widget.height,
|
onPressed: onTap,
|
||||||
color: widget.color,
|
child: Container(
|
||||||
child: Center(
|
width: widget.width,
|
||||||
child: widget.child,
|
height: widget.height,
|
||||||
|
color: widget.color,
|
||||||
|
child: Center(
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||||
import 'package:stackwallet/utilities/show_loading.dart';
|
import 'package:stackwallet/utilities/show_loading.dart';
|
||||||
import 'package:stackwallet/utilities/util.dart';
|
import 'package:stackwallet/utilities/util.dart';
|
||||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||||
|
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||||
|
import 'package:stackwallet/widgets/dialogs/basic_dialog.dart';
|
||||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||||
import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart';
|
import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
@ -37,7 +39,7 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
final bool popPrevious;
|
final bool popPrevious;
|
||||||
final NavigatorState? desktopNavigatorState;
|
final NavigatorState? desktopNavigatorState;
|
||||||
|
|
||||||
Future<void> _loadTokenWallet(
|
Future<bool> _loadTokenWallet(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
Manager manager,
|
Manager manager,
|
||||||
|
@ -52,7 +54,31 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await ref.read(tokenServiceProvider)!.initialize();
|
try {
|
||||||
|
await ref.read(tokenServiceProvider)!.initialize();
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
await showDialog<void>(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => BasicDialog(
|
||||||
|
title: "Failed to load token data",
|
||||||
|
desktopHeight: double.infinity,
|
||||||
|
desktopWidth: 450,
|
||||||
|
rightButton: PrimaryButton(
|
||||||
|
label: "OK",
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
if (desktopNavigatorState == null) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _openWallet(BuildContext context, WidgetRef ref) async {
|
void _openWallet(BuildContext context, WidgetRef ref) async {
|
||||||
|
@ -91,13 +117,21 @@ class SimpleWalletCard extends ConsumerWidget {
|
||||||
final contract =
|
final contract =
|
||||||
ref.read(mainDBProvider).getEthContractSync(contractAddress!)!;
|
ref.read(mainDBProvider).getEthContractSync(contractAddress!)!;
|
||||||
|
|
||||||
await showLoading<void>(
|
final success = await showLoading<bool>(
|
||||||
whileFuture: _loadTokenWallet(context, ref, manager, contract),
|
whileFuture: _loadTokenWallet(
|
||||||
context: context,
|
desktopNavigatorState?.context ?? context,
|
||||||
|
ref,
|
||||||
|
manager,
|
||||||
|
contract),
|
||||||
|
context: desktopNavigatorState?.context ?? context,
|
||||||
opaqueBG: true,
|
opaqueBG: true,
|
||||||
message: "Loading ${contract.name}",
|
message: "Loading ${contract.name}",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (desktopNavigatorState == null) {
|
if (desktopNavigatorState == null) {
|
||||||
// pop loading
|
// pop loading
|
||||||
nav.pop();
|
nav.pop();
|
||||||
|
|
|
@ -11,7 +11,7 @@ description: Stack Wallet
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 1.7.4+164
|
version: 1.7.4+165
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
|
|
|
@ -7,9 +7,13 @@ import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
|
||||||
import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart';
|
import 'package:stackwallet/widgets/custom_pin_put/pin_keyboard.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group("CustomPinPut tests", () {
|
group("CustomPinPut tests, non-random PIN", () {
|
||||||
testWidgets("CustomPinPut with 4 fields builds correctly", (tester) async {
|
testWidgets("CustomPinPut with 4 fields builds correctly, non-random PIN",
|
||||||
const pinPut = CustomPinPut(fieldsCount: 4);
|
(tester) async {
|
||||||
|
const pinPut = CustomPinPut(
|
||||||
|
fieldsCount: 4,
|
||||||
|
isRandom: false,
|
||||||
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
|
@ -31,12 +35,17 @@ void main() {
|
||||||
expect(find.byType(NumberKey), findsNWidgets(10));
|
expect(find.byType(NumberKey), findsNWidgets(10));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("CustomPinPut entering a pin successfully", (tester) async {
|
testWidgets("CustomPinPut entering a pin successfully, non-random PIN",
|
||||||
|
(tester) async {
|
||||||
bool submittedPinMatches = false;
|
bool submittedPinMatches = false;
|
||||||
final pinPut = CustomPinPut(
|
final pinPut = CustomPinPut(
|
||||||
fieldsCount: 4,
|
fieldsCount: 4,
|
||||||
onSubmit: (pin) => submittedPinMatches = pin == "1234",
|
onSubmit: (pin) {
|
||||||
|
submittedPinMatches = pin == "1234";
|
||||||
|
print("pin entered: $pin");
|
||||||
|
},
|
||||||
useNativeKeyboard: false,
|
useNativeKeyboard: false,
|
||||||
|
isRandom: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -69,16 +78,20 @@ void main() {
|
||||||
await tester.tap(find.byWidgetPredicate(
|
await tester.tap(find.byWidgetPredicate(
|
||||||
(widget) => widget is NumberKey && widget.number == "4"));
|
(widget) => widget is NumberKey && widget.number == "4"));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byType(SubmitKey));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(submittedPinMatches, true);
|
expect(submittedPinMatches, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("CustomPinPut pin enter fade animation", (tester) async {
|
testWidgets("CustomPinPut pin enter fade animation, non-random PIN",
|
||||||
|
(tester) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final pinPut = CustomPinPut(
|
final pinPut = CustomPinPut(
|
||||||
fieldsCount: 4,
|
fieldsCount: 4,
|
||||||
pinAnimationType: PinAnimationType.fade,
|
pinAnimationType: PinAnimationType.fade,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
isRandom: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -104,12 +117,14 @@ void main() {
|
||||||
expect(controller.text, "");
|
expect(controller.text, "");
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("CustomPinPut pin enter scale animation", (tester) async {
|
testWidgets("CustomPinPut pin enter scale animation, non-random PIN",
|
||||||
|
(tester) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final pinPut = CustomPinPut(
|
final pinPut = CustomPinPut(
|
||||||
fieldsCount: 4,
|
fieldsCount: 4,
|
||||||
pinAnimationType: PinAnimationType.scale,
|
pinAnimationType: PinAnimationType.scale,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
isRandom: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -135,12 +150,14 @@ void main() {
|
||||||
expect(controller.text, "");
|
expect(controller.text, "");
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("CustomPinPut pin enter rotate animation", (tester) async {
|
testWidgets("CustomPinPut pin enter rotate animation, non-random PIN",
|
||||||
|
(tester) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
final pinPut = CustomPinPut(
|
final pinPut = CustomPinPut(
|
||||||
fieldsCount: 4,
|
fieldsCount: 4,
|
||||||
pinAnimationType: PinAnimationType.rotation,
|
pinAnimationType: PinAnimationType.rotation,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
|
isRandom: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -167,11 +184,12 @@ void main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets("PinKeyboard builds correctly", (tester) async {
|
testWidgets("PinKeyboard builds correctly, non-random PIN", (tester) async {
|
||||||
final keyboard = PinKeyboard(
|
final keyboard = PinKeyboard(
|
||||||
onNumberKeyPressed: (_) {},
|
onNumberKeyPressed: (_) {},
|
||||||
onBackPressed: () {},
|
onBackPressed: () {},
|
||||||
onSubmitPressed: () {},
|
onSubmitPressed: () {},
|
||||||
|
isRandom: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -188,6 +206,7 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(find.byType(BackspaceKey), findsOneWidget);
|
expect(find.byType(BackspaceKey), findsOneWidget);
|
||||||
|
expect(find.byType(SubmitKey), findsOneWidget);
|
||||||
expect(find.byType(NumberKey), findsNWidgets(10));
|
expect(find.byType(NumberKey), findsNWidgets(10));
|
||||||
expect(find.text("0"), findsOneWidget);
|
expect(find.text("0"), findsOneWidget);
|
||||||
expect(find.text("1"), findsOneWidget);
|
expect(find.text("1"), findsOneWidget);
|
||||||
|
@ -199,6 +218,220 @@ void main() {
|
||||||
expect(find.text("7"), findsOneWidget);
|
expect(find.text("7"), findsOneWidget);
|
||||||
expect(find.text("8"), findsOneWidget);
|
expect(find.text("8"), findsOneWidget);
|
||||||
expect(find.text("9"), findsOneWidget);
|
expect(find.text("9"), findsOneWidget);
|
||||||
expect(find.byType(SvgPicture), findsOneWidget);
|
expect(find.byType(SvgPicture), findsNWidgets(2));
|
||||||
|
});
|
||||||
|
|
||||||
|
group("CustomPinPut tests, with random PIN", () {
|
||||||
|
testWidgets("CustomPinPut with 4 fields builds correctly, with random PIN",
|
||||||
|
(tester) async {
|
||||||
|
const pinPut = CustomPinPut(
|
||||||
|
fieldsCount: 4,
|
||||||
|
isRandom: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: const Material(
|
||||||
|
child: pinPut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// expects 5 here. Four + the actual text field text
|
||||||
|
expect(find.text(""), findsNWidgets(5));
|
||||||
|
expect(find.byType(PinKeyboard), findsOneWidget);
|
||||||
|
expect(find.byType(BackspaceKey), findsOneWidget);
|
||||||
|
expect(find.byType(NumberKey), findsNWidgets(10));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("CustomPinPut entering a pin successfully, with random PIN",
|
||||||
|
(tester) async {
|
||||||
|
bool submittedPinMatches = false;
|
||||||
|
final pinPut = CustomPinPut(
|
||||||
|
fieldsCount: 4,
|
||||||
|
onSubmit: (pin) {
|
||||||
|
submittedPinMatches = pin == "1234";
|
||||||
|
print("pin entered: $pin");
|
||||||
|
},
|
||||||
|
useNativeKeyboard: false,
|
||||||
|
isRandom: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: pinPut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "1"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "2"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "6"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byType(BackspaceKey));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "3"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "4"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.byType(SubmitKey));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(submittedPinMatches, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("CustomPinPut pin enter fade animation, with random PIN",
|
||||||
|
(tester) async {
|
||||||
|
final controller = TextEditingController();
|
||||||
|
final pinPut = CustomPinPut(
|
||||||
|
fieldsCount: 4,
|
||||||
|
pinAnimationType: PinAnimationType.fade,
|
||||||
|
controller: controller,
|
||||||
|
isRandom: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: pinPut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "1"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(controller.text, "1");
|
||||||
|
|
||||||
|
await tester.tap(find.byType(BackspaceKey));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(controller.text, "");
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("CustomPinPut pin enter scale animation, with random PIN",
|
||||||
|
(tester) async {
|
||||||
|
final controller = TextEditingController();
|
||||||
|
final pinPut = CustomPinPut(
|
||||||
|
fieldsCount: 4,
|
||||||
|
pinAnimationType: PinAnimationType.scale,
|
||||||
|
controller: controller,
|
||||||
|
isRandom: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: pinPut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "1"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(controller.text, "1");
|
||||||
|
|
||||||
|
await tester.tap(find.byType(BackspaceKey));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(controller.text, "");
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("CustomPinPut pin enter rotate animation, with random PIN",
|
||||||
|
(tester) async {
|
||||||
|
final controller = TextEditingController();
|
||||||
|
final pinPut = CustomPinPut(
|
||||||
|
fieldsCount: 4,
|
||||||
|
pinAnimationType: PinAnimationType.rotation,
|
||||||
|
controller: controller,
|
||||||
|
isRandom: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: pinPut,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.byWidgetPredicate(
|
||||||
|
(widget) => widget is NumberKey && widget.number == "1"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(controller.text, "1");
|
||||||
|
|
||||||
|
await tester.tap(find.byType(BackspaceKey));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(controller.text, "");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets("PinKeyboard builds correctly, with random PIN", (tester) async {
|
||||||
|
final keyboard = PinKeyboard(
|
||||||
|
onNumberKeyPressed: (_) {},
|
||||||
|
onBackPressed: () {},
|
||||||
|
onSubmitPressed: () {},
|
||||||
|
isRandom: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
theme: ThemeData(
|
||||||
|
extensions: [
|
||||||
|
StackColors.fromStackColorTheme(LightColors()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
home: Material(
|
||||||
|
child: keyboard,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(BackspaceKey), findsOneWidget);
|
||||||
|
expect(find.byType(SubmitKey), findsOneWidget);
|
||||||
|
expect(find.byType(NumberKey), findsNWidgets(10));
|
||||||
|
expect(find.text("0"), findsOneWidget);
|
||||||
|
expect(find.text("1"), findsOneWidget);
|
||||||
|
expect(find.text("2"), findsOneWidget);
|
||||||
|
expect(find.text("3"), findsOneWidget);
|
||||||
|
expect(find.text("4"), findsOneWidget);
|
||||||
|
expect(find.text("5"), findsOneWidget);
|
||||||
|
expect(find.text("6"), findsOneWidget);
|
||||||
|
expect(find.text("7"), findsOneWidget);
|
||||||
|
expect(find.text("8"), findsOneWidget);
|
||||||
|
expect(find.text("9"), findsOneWidget);
|
||||||
|
expect(find.byType(SvgPicture), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue