mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
commit
972494fc94
25 changed files with 1664 additions and 348 deletions
|
@ -77,12 +77,12 @@ void main() async {
|
|||
}
|
||||
|
||||
Screen? screen;
|
||||
if (Platform.isLinux || Util.isDesktop) {
|
||||
if (Platform.isLinux || (Util.isDesktop && !Platform.isIOS)) {
|
||||
screen = await getCurrentScreen();
|
||||
Util.screenWidth = screen?.frame.width;
|
||||
}
|
||||
|
||||
if (Util.isDesktop) {
|
||||
if (Util.isDesktop && !Platform.isIOS) {
|
||||
setWindowTitle('Stack Wallet');
|
||||
setWindowMinSize(const Size(1220, 100));
|
||||
setWindowMaxSize(Size.infinite);
|
||||
|
|
|
@ -33,6 +33,19 @@ class AddressEntryData extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get isEmpty {
|
||||
if (address != null && address!.isNotEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (addressLabel != null && addressLabel!.isNotEmpty) {
|
||||
return false;
|
||||
}
|
||||
if (coin != null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get isValid {
|
||||
if (_address == null || coin == null || _addressLabel == null) {
|
||||
return false;
|
||||
|
|
|
@ -2,13 +2,16 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
|
||||
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/recovery_phrase_explanation_dialog.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/coins/coin_service.dart';
|
||||
import 'package:stackwallet/services/coins/manager.dart';
|
||||
import 'package:stackwallet/services/transaction_notification_tracker.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/default_nodes.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
@ -20,6 +23,7 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
|||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
|
@ -74,6 +78,31 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
)
|
||||
: AppBar(
|
||||
leading: const AppBarBackButton(),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
right: 10,
|
||||
),
|
||||
child: AppBarIconButton(
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.circleQuestion,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
onPressed: () async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => const RecoveryPhraseExplanationDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.all(isDesktop ? 0 : 16),
|
||||
|
@ -113,18 +142,183 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
height: isDesktop ? 32 : 16,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(32)
|
||||
: const EdgeInsets.all(12),
|
||||
padding: const EdgeInsets.all(32),
|
||||
width: isDesktop ? 480 : null,
|
||||
child: Text(
|
||||
"On the next screen you will see $_numberOfPhraseWords words that make up your recovery phrase.\n\nPlease write it down. Keep it safe and never share it with anyone. Your recovery phrase is the only way you can access your funds if you forget your PIN, lose your phone, etc.\n\nStack Wallet does not keep nor is able to restore your recover phrase. Only you have access to your wallet.",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextMediumRegular(context)
|
||||
: STextStyles.subtitle(context).copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
child: isDesktop
|
||||
? Text(
|
||||
"On the next screen you will see $_numberOfPhraseWords words that make up your recovery phrase.\n\nPlease write it down. Keep it safe and never share it with anyone. Your recovery phrase is the only way you can access your funds if you forget your PIN, lose your phone, etc.\n\nStack Wallet does not keep nor is able to restore your recover phrase. Only you have access to your wallet.",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextMediumRegular(context)
|
||||
: STextStyles.subtitle(context).copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Important",
|
||||
style: STextStyles.desktopH3(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
style: STextStyles.desktopH3(context)
|
||||
.copyWith(fontSize: 18),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "On the next screen you will be given ",
|
||||
style: STextStyles.desktopH3(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "12 words",
|
||||
style: STextStyles.desktopH3(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ". They are your ",
|
||||
style: STextStyles.desktopH3(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "recovery phrase",
|
||||
style: STextStyles.desktopH3(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ".",
|
||||
style: STextStyles.desktopH3(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: RoundedContainer(
|
||||
radiusMultiplier: 20,
|
||||
padding: const EdgeInsets.all(9),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.pencil,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Text(
|
||||
"Write them down.",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: RoundedContainer(
|
||||
radiusMultiplier: 20,
|
||||
padding: const EdgeInsets.all(8),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.lock,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Text(
|
||||
"Keep them safe.",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: RoundedContainer(
|
||||
radiusMultiplier: 20,
|
||||
padding: const EdgeInsets.all(8),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonBackSecondary,
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
Text(
|
||||
"Do not show them to anyone.",
|
||||
style: STextStyles.navBarTitle(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!isDesktop) const Spacer(),
|
||||
if (isDesktop)
|
||||
|
@ -150,27 +344,30 @@ class _NewWalletRecoveryPhraseWarningViewState
|
|||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
crossAxisAlignment: isDesktop
|
||||
? CrossAxisAlignment.start
|
||||
: CrossAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Checkbox(
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
value: ref
|
||||
.watch(checkBoxStateProvider.state)
|
||||
.state,
|
||||
onChanged: (newValue) {
|
||||
ref.read(checkBoxStateProvider.state).state =
|
||||
newValue!;
|
||||
},
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: Checkbox(
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
value: ref
|
||||
.watch(checkBoxStateProvider.state)
|
||||
.state,
|
||||
onChanged: (newValue) {
|
||||
ref
|
||||
.read(checkBoxStateProvider.state)
|
||||
.state = newValue!;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: isDesktop ? 14 : 4,
|
||||
width: isDesktop ? 20 : 10,
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
"I understand that if I lose my recovery phrase, I will not be able to access my funds.",
|
||||
"I understand that Stack Wallet does not keep and cannot restore your recovery phrase, and If I lose my recovery phrase, I will not be able to access my funds.",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextMedium(context)
|
||||
: STextStyles.baseXS(context),
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
||||
class RecoveryPhraseExplanationDialog extends StatelessWidget {
|
||||
const RecoveryPhraseExplanationDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StackDialogBase(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height -
|
||||
16 -
|
||||
16 -
|
||||
24 -
|
||||
24 -
|
||||
24 -
|
||||
76,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Recovery phrase explained",
|
||||
style: STextStyles.titleBold12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Text(
|
||||
"The car analogy.",
|
||||
style: STextStyles.titleBold12(context).copyWith(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"You can drive your car when your have your car keys. If you lose your car keys, you can’t drive you car. If someone steals your car keys, they can steal your car. If someone copies your car keys, they can also steal your car.",
|
||||
style: STextStyles.baseXS(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"Cryptocurrency works the same way. These recovery phrase words we’re about to show you are like the car keys in the metaphor above.",
|
||||
style: STextStyles.baseXS(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"If you lose these words, you can’t access your money. If someone steals these words, they can access and steal your money. If someone copies these words, they can access and steal your money.",
|
||||
style: STextStyles.baseXS(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
"If your funds are lost or stolen because of you don’t write down your recovery phrase or keep it safe, then NOBODY, NOT EVEN US, can help you recover your money.",
|
||||
style: STextStyles.baseXS(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
child: SecondaryButton(
|
||||
label: "Close",
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -168,8 +168,9 @@ class _FixedRateMarketPairCoinSelectionViewState
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: Util.isDesktop ? false : true,
|
||||
enableSuggestions: Util.isDesktop ? false : true,
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: filter,
|
||||
|
|
|
@ -125,6 +125,7 @@ class _FloatingRateCurrencySelectionViewState
|
|||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
|
|
|
@ -1359,8 +1359,11 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
|
|||
SizedBox(
|
||||
height: isDesktop ? 20 : 12,
|
||||
),
|
||||
RateTypeToggle(
|
||||
onChanged: onRateTypeChanged,
|
||||
SizedBox(
|
||||
height: 60,
|
||||
child: RateTypeToggle(
|
||||
onChanged: onRateTypeChanged,
|
||||
),
|
||||
),
|
||||
if (ref.read(exchangeFormStateProvider).fromAmount != null &&
|
||||
ref.read(exchangeFormStateProvider).fromAmount != Decimal.zero)
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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/rounded_container.dart';
|
||||
import 'package:stackwallet/widgets/toggle.dart';
|
||||
|
||||
import '../../../utilities/constants.dart';
|
||||
|
||||
class RateTypeToggle extends ConsumerWidget {
|
||||
const RateTypeToggle({
|
||||
|
@ -28,177 +26,203 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.estimated;
|
||||
|
||||
return RoundedContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
color: isDesktop
|
||||
return Toggle(
|
||||
onValueChanged: (value) {
|
||||
if (!estimated) {
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
ExchangeRateType.estimated;
|
||||
onChanged?.call(ExchangeRateType.estimated);
|
||||
} else {
|
||||
onChanged?.call(ExchangeRateType.fixed);
|
||||
}
|
||||
},
|
||||
isOn: !estimated,
|
||||
onColor: Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
|
||||
offColor: isDesktop
|
||||
? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
|
||||
: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: estimated
|
||||
? SystemMouseCursors.basic
|
||||
: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (!estimated) {
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
ExchangeRateType.estimated;
|
||||
onChanged?.call(ExchangeRateType.estimated);
|
||||
}
|
||||
},
|
||||
child: RoundedContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(17)
|
||||
: const EdgeInsets.all(12),
|
||||
color: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG
|
||||
: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.lockOpen,
|
||||
width: 12,
|
||||
height: 14,
|
||||
color: isDesktop
|
||||
? estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary
|
||||
: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text(
|
||||
"Estimate rate",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
)
|
||||
: STextStyles.smallMed12(context).copyWith(
|
||||
color: estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: !estimated
|
||||
? SystemMouseCursors.basic
|
||||
: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (estimated) {
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
ExchangeRateType.fixed;
|
||||
onChanged?.call(ExchangeRateType.fixed);
|
||||
}
|
||||
},
|
||||
child: RoundedContainer(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(17)
|
||||
: const EdgeInsets.all(12),
|
||||
color: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG
|
||||
: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.lock,
|
||||
width: 12,
|
||||
height: 14,
|
||||
color: isDesktop
|
||||
? !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary
|
||||
: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text(
|
||||
"Fixed rate",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
)
|
||||
: STextStyles.smallMed12(context).copyWith(
|
||||
color: !estimated
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onIcon: Assets.svg.lockOpen,
|
||||
onText: "Estimate rate",
|
||||
offIcon: Assets.svg.lock,
|
||||
offText: "Fixed rate",
|
||||
);
|
||||
//
|
||||
// return RoundedContainer(
|
||||
// padding: const EdgeInsets.all(0),
|
||||
// color: isDesktop
|
||||
// ? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
|
||||
// : Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: ConditionalParent(
|
||||
// condition: isDesktop,
|
||||
// builder: (child) => MouseRegion(
|
||||
// cursor: estimated
|
||||
// ? SystemMouseCursors.basic
|
||||
// : SystemMouseCursors.click,
|
||||
// child: child,
|
||||
// ),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// if (!estimated) {
|
||||
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
// ExchangeRateType.estimated;
|
||||
// onChanged?.call(ExchangeRateType.estimated);
|
||||
// }
|
||||
// },
|
||||
// child: RoundedContainer(
|
||||
// padding: isDesktop
|
||||
// ? const EdgeInsets.all(17)
|
||||
// : const EdgeInsets.all(12),
|
||||
// color: estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldDefaultBG
|
||||
// : Colors.transparent,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// Assets.svg.lockOpen,
|
||||
// width: 12,
|
||||
// height: 14,
|
||||
// color: isDesktop
|
||||
// ? estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary
|
||||
// : estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// width: 5,
|
||||
// ),
|
||||
// Text(
|
||||
// "Estimate rate",
|
||||
// style: isDesktop
|
||||
// ? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
// .copyWith(
|
||||
// color: estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary,
|
||||
// )
|
||||
// : STextStyles.smallMed12(context).copyWith(
|
||||
// color: estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: ConditionalParent(
|
||||
// condition: isDesktop,
|
||||
// builder: (child) => MouseRegion(
|
||||
// cursor: !estimated
|
||||
// ? SystemMouseCursors.basic
|
||||
// : SystemMouseCursors.click,
|
||||
// child: child,
|
||||
// ),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// if (estimated) {
|
||||
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
// ExchangeRateType.fixed;
|
||||
// onChanged?.call(ExchangeRateType.fixed);
|
||||
// }
|
||||
// },
|
||||
// child: RoundedContainer(
|
||||
// padding: isDesktop
|
||||
// ? const EdgeInsets.all(17)
|
||||
// : const EdgeInsets.all(12),
|
||||
// color: !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldDefaultBG
|
||||
// : Colors.transparent,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// Assets.svg.lock,
|
||||
// width: 12,
|
||||
// height: 14,
|
||||
// color: isDesktop
|
||||
// ? !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary
|
||||
// : !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// width: 5,
|
||||
// ),
|
||||
// Text(
|
||||
// "Fixed rate",
|
||||
// style: isDesktop
|
||||
// ? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
// .copyWith(
|
||||
// color: !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary,
|
||||
// )
|
||||
// : STextStyles.smallMed12(context).copyWith(
|
||||
// color: !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,8 +214,9 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RoundedWhiteContainer(
|
||||
borderColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
borderColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.backgroundAppBar,
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView(
|
||||
primary: false,
|
||||
|
@ -281,7 +282,7 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
? BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
.backgroundAppBar,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -1115,31 +1116,40 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
|
|||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
late final String url;
|
||||
switch (trade.exchangeName) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
url =
|
||||
"https://changenow.io/exchange/txs/${trade.tradeId}";
|
||||
break;
|
||||
case SimpleSwapExchange.exchangeName:
|
||||
url =
|
||||
"https://simpleswap.io/exchange?id=${trade.tradeId}";
|
||||
break;
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse(url),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
url,
|
||||
style: STextStyles.link2(context),
|
||||
),
|
||||
);
|
||||
}),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
late final String url;
|
||||
switch (trade.exchangeName) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
url =
|
||||
"https://changenow.io/exchange/txs/${trade.tradeId}";
|
||||
break;
|
||||
case SimpleSwapExchange.exchangeName:
|
||||
url =
|
||||
"https://simpleswap.io/exchange?id=${trade.tradeId}";
|
||||
break;
|
||||
}
|
||||
return ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
Uri.parse(url),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
url,
|
||||
style: STextStyles.link2(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1188,7 +1198,7 @@ class _Divider extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,9 +109,7 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
|
|||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
// maxHeight:
|
||||
// MediaQuery.of(context).size.height - 64,
|
||||
maxHeight: double.infinity,
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
|
@ -16,7 +16,6 @@ import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
|
|||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
|
|
@ -358,7 +358,7 @@ class _TransactionDetailsViewState
|
|||
borderColor: isDesktop
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background
|
||||
.backgroundAppBar
|
||||
: null,
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: child,
|
||||
|
@ -382,7 +382,7 @@ class _TransactionDetailsViewState
|
|||
? BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
.backgroundAppBar,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -1387,7 +1387,7 @@ class _Divider extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
color: Theme.of(context).extension<StackColors>()!.backgroundAppBar,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,569 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
|
||||
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
|
||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/format.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/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/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../route_generator.dart';
|
||||
|
||||
class DesktopAllTradesView extends ConsumerStatefulWidget {
|
||||
const DesktopAllTradesView({Key? key}) : super(key: key);
|
||||
|
||||
static const String routeName = "/desktopAllTrades";
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopAllTradesView> createState() =>
|
||||
_DesktopAllTradesViewState();
|
||||
}
|
||||
|
||||
class _DesktopAllTradesViewState extends ConsumerState<DesktopAllTradesView> {
|
||||
late final TextEditingController _searchController;
|
||||
late final FocusNode searchFieldFocusNode;
|
||||
|
||||
String _searchString = "";
|
||||
|
||||
List<Tuple2<String, List<Trade>>> groupTransactionsByMonth(
|
||||
List<Trade> trades) {
|
||||
Map<String, List<Trade>> map = {};
|
||||
|
||||
for (var trade in trades) {
|
||||
final date = trade.timestamp;
|
||||
final monthYear = "${Constants.monthMap[date.month]} ${date.year}";
|
||||
if (map[monthYear] == null) {
|
||||
map[monthYear] = [];
|
||||
}
|
||||
map[monthYear]!.add(trade);
|
||||
}
|
||||
|
||||
List<Tuple2<String, List<Trade>>> result = [];
|
||||
map.forEach((key, value) {
|
||||
result.add(Tuple2(key, value));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
searchFieldFocusNode = FocusNode();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
searchFieldFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopScaffold(
|
||||
appBar: DesktopAppBar(
|
||||
isCompactHeight: true,
|
||||
background: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
leading: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 32,
|
||||
),
|
||||
AppBarIconButton(
|
||||
size: 32,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
shadows: const [],
|
||||
icon: SvgPicture.asset(
|
||||
Assets.svg.arrowLeft,
|
||||
width: 18,
|
||||
height: 18,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.topNavIconPrimary,
|
||||
),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Text(
|
||||
"Trades",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 20,
|
||||
top: 20,
|
||||
right: 20,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 570,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
controller: _searchController,
|
||||
focusNode: searchFieldFocusNode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchString = value;
|
||||
});
|
||||
},
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
height: 1.8,
|
||||
),
|
||||
decoration: standardInputDecoration(
|
||||
"Search...",
|
||||
searchFieldFocusNode,
|
||||
context,
|
||||
desktopMed: true,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 18,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
_searchString = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Expanded(
|
||||
child: Consumer(
|
||||
builder: (_, ref, __) {
|
||||
List<Trade> trades = ref.watch(
|
||||
tradesServiceProvider.select((value) => value.trades));
|
||||
|
||||
if (_searchString.isNotEmpty) {
|
||||
final term = _searchString.toLowerCase();
|
||||
trades = trades
|
||||
.where((e) => e.toString().toLowerCase().contains(term))
|
||||
.toList(growable: false);
|
||||
}
|
||||
final monthlyList = groupTransactionsByMonth(trades);
|
||||
|
||||
return ListView.builder(
|
||||
primary: false,
|
||||
itemCount: monthlyList.length,
|
||||
itemBuilder: (_, index) {
|
||||
final month = monthlyList[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (index != 0)
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Text(
|
||||
month.item1,
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
primary: false,
|
||||
separatorBuilder: (context, _) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.background,
|
||||
),
|
||||
itemCount: month.item2.length,
|
||||
itemBuilder: (context, index) => Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: DesktopTradeRowCard(
|
||||
key: Key(
|
||||
"transactionCard_key_${month.item2[index].tradeId}"),
|
||||
tradeId: month.item2[index].tradeId,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopTradeRowCard extends ConsumerStatefulWidget {
|
||||
const DesktopTradeRowCard({
|
||||
Key? key,
|
||||
required this.tradeId,
|
||||
}) : super(key: key);
|
||||
|
||||
final String tradeId;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopTradeRowCard> createState() =>
|
||||
_DesktopTradeRowCardState();
|
||||
}
|
||||
|
||||
class _DesktopTradeRowCardState extends ConsumerState<DesktopTradeRowCard> {
|
||||
late final String tradeId;
|
||||
|
||||
String _fetchIconAssetForStatus(String statusString, BuildContext context) {
|
||||
ChangeNowTransactionStatus? status;
|
||||
try {
|
||||
if (statusString.toLowerCase().startsWith("waiting")) {
|
||||
statusString = "waiting";
|
||||
}
|
||||
status = changeNowTransactionStatusFromStringIgnoreCase(statusString);
|
||||
} on ArgumentError catch (_) {
|
||||
status = ChangeNowTransactionStatus.Failed;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case ChangeNowTransactionStatus.New:
|
||||
case ChangeNowTransactionStatus.Waiting:
|
||||
case ChangeNowTransactionStatus.Confirming:
|
||||
case ChangeNowTransactionStatus.Exchanging:
|
||||
case ChangeNowTransactionStatus.Sending:
|
||||
case ChangeNowTransactionStatus.Refunded:
|
||||
case ChangeNowTransactionStatus.Verifying:
|
||||
return Assets.svg.txExchangePending(context);
|
||||
case ChangeNowTransactionStatus.Finished:
|
||||
return Assets.svg.txExchange(context);
|
||||
case ChangeNowTransactionStatus.Failed:
|
||||
return Assets.svg.txExchangeFailed(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
tradeId = widget.tradeId;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final String? txid =
|
||||
ref.read(tradeSentFromStackLookupProvider).getTxidForTradeId(tradeId);
|
||||
final List<String>? walletIds = ref
|
||||
.read(tradeSentFromStackLookupProvider)
|
||||
.getWalletIdsForTradeId(tradeId);
|
||||
|
||||
final trade =
|
||||
ref.watch(tradesServiceProvider.select((value) => value.get(tradeId)))!;
|
||||
|
||||
return Material(
|
||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(Constants.size.circularBorderRadius),
|
||||
),
|
||||
child: RawMaterialButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (txid != null && walletIds != null && walletIds.isNotEmpty) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(walletIds.first);
|
||||
|
||||
debugPrint("name: ${manager.walletName}");
|
||||
|
||||
// TODO store tx data completely locally in isar so we don't lock up ui here when querying txData
|
||||
final txData = await manager.transactionData;
|
||||
|
||||
final tx = txData.getAllTransactions()[txid];
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => DesktopDialog(
|
||||
maxHeight: MediaQuery.of(context).size.height - 64,
|
||||
maxWidth: 580,
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack: tx,
|
||||
walletName: manager.walletName,
|
||||
walletId: walletIds.first,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: TradeDetailsView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Trade details",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
primary: false,
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack: tx,
|
||||
walletName: manager.walletName,
|
||||
walletId: walletIds.first,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TradeDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
unawaited(
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => Navigator(
|
||||
initialRoute: TradeDetailsView.routeName,
|
||||
onGenerateRoute: RouteGenerator.generateRoute,
|
||||
onGenerateInitialRoutes: (_, __) {
|
||||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Trade details",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
DesktopDialogCloseButton(
|
||||
onPressedOverride: Navigator.of(
|
||||
context,
|
||||
rootNavigator: true,
|
||||
).pop,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
primary: false,
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack: null,
|
||||
walletName: null,
|
||||
walletId: walletIds?.first,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const RouteSettings(
|
||||
name: TradeDetailsView.routeName,
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
_fetchIconAssetForStatus(
|
||||
trade.status,
|
||||
context,
|
||||
),
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
"${trade.payInCurrency.toUpperCase()} → ${trade.payOutCurrency.toUpperCase()}",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
Format.extractDateFrom(
|
||||
trade.timestamp.millisecondsSinceEpoch ~/ 1000),
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
child: Text(
|
||||
"-${Decimal.tryParse(trade.payInAmount)?.toStringAsFixed(8) ?? "..."} ${trade.payInCurrency.toUpperCase()}",
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
trade.exchangeName,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.circleInfo,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textSubtitle2,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,19 +3,20 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart';
|
||||
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
|
||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/trade_card.dart';
|
||||
|
||||
import '../../../route_generator.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog.dart';
|
||||
import '../../../widgets/desktop/desktop_dialog_close_button.dart';
|
||||
|
||||
class DesktopTradeHistory extends ConsumerStatefulWidget {
|
||||
const DesktopTradeHistory({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -60,9 +61,21 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Exchange details",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Recent trades",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
BlueTextButton(
|
||||
text: "See all",
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.pushNamed(DesktopAllTradesView.routeName);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
|
@ -126,9 +139,7 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
|
|||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
// maxHeight:
|
||||
// MediaQuery.of(context).size.height - 64,
|
||||
maxHeight: double.infinity,
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -159,11 +170,14 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
|
|||
),
|
||||
),
|
||||
Flexible(
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack: tx,
|
||||
walletName: manager.walletName,
|
||||
walletId: walletIds.first,
|
||||
child: SingleChildScrollView(
|
||||
primary: false,
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack: tx,
|
||||
walletName: manager.walletName,
|
||||
walletId: walletIds.first,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -189,9 +203,7 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
|
|||
return [
|
||||
FadePageRoute(
|
||||
DesktopDialog(
|
||||
// maxHeight:
|
||||
// MediaQuery.of(context).size.height - 64,
|
||||
maxHeight: double.infinity,
|
||||
maxHeight: null,
|
||||
maxWidth: 580,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -222,11 +234,15 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
|
|||
),
|
||||
),
|
||||
Flexible(
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack: null,
|
||||
walletName: null,
|
||||
walletId: walletIds?.first,
|
||||
child: SingleChildScrollView(
|
||||
primary: false,
|
||||
child: TradeDetailsView(
|
||||
tradeId: tradeId,
|
||||
transactionIfSentFromStack:
|
||||
null,
|
||||
walletName: null,
|
||||
walletId: walletIds?.first,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -258,13 +274,26 @@ class _DesktopTradeHistoryState extends ConsumerState<DesktopTradeHistory> {
|
|||
],
|
||||
);
|
||||
} else {
|
||||
return RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Trades will appear here",
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Recent trades",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Trades will appear here",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,19 +140,23 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
|
|||
ref.watch(addressBookServiceProvider.select((value) => value.contacts));
|
||||
|
||||
final allContacts = contacts
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(addressBookFilterProvider
|
||||
.select((value) => value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((element) =>
|
||||
element.addresses.isEmpty ||
|
||||
element.addresses
|
||||
.where((e) => ref.watch(addressBookFilterProvider
|
||||
.select((value) => value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where(
|
||||
(e) => ref.read(addressBookServiceProvider).matches(_searchTerm, e))
|
||||
.toList();
|
||||
|
||||
final favorites = contacts
|
||||
.where((element) => element.addresses
|
||||
.where((e) => ref.watch(addressBookFilterProvider
|
||||
.select((value) => value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((element) =>
|
||||
element.addresses.isEmpty ||
|
||||
element.addresses
|
||||
.where((e) => ref.watch(addressBookFilterProvider
|
||||
.select((value) => value.coins.contains(e.coin))))
|
||||
.isNotEmpty)
|
||||
.where((e) =>
|
||||
e.isFavorite &&
|
||||
ref.read(addressBookServiceProvider).matches(_searchTerm, e))
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu_item.dart';
|
||||
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
@ -191,29 +190,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
|||
),
|
||||
DesktopMenuItem(
|
||||
duration: duration,
|
||||
icon: SvgPicture.asset(
|
||||
ref.watch(notificationsProvider.select(
|
||||
(value) => value.hasUnreadNotifications))
|
||||
? Assets.svg.bellNew(context)
|
||||
: Assets.svg.bell,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: ref.watch(notificationsProvider.select(
|
||||
(value) => value.hasUnreadNotifications))
|
||||
? null
|
||||
: DesktopMenuItemId.notifications ==
|
||||
ref
|
||||
.watch(currentDesktopMenuItemProvider
|
||||
.state)
|
||||
.state
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark
|
||||
.withOpacity(0.8),
|
||||
),
|
||||
icon: const DesktopNotificationsIcon(),
|
||||
label: "Notifications",
|
||||
value: DesktopMenuItemId.notifications,
|
||||
onChanged: updateSelectedMenuItem,
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu.dart';
|
||||
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
|
||||
import 'package:stackwallet/providers/global/notifications_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
||||
|
@ -11,6 +15,39 @@ class DMIController {
|
|||
}
|
||||
}
|
||||
|
||||
class DesktopNotificationsIcon extends ConsumerStatefulWidget {
|
||||
const DesktopNotificationsIcon({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopNotificationsIcon> createState() =>
|
||||
_DesktopNotificationsIconState();
|
||||
}
|
||||
|
||||
class _DesktopNotificationsIconState
|
||||
extends ConsumerState<DesktopNotificationsIcon> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SvgPicture.asset(
|
||||
ref.watch(notificationsProvider
|
||||
.select((value) => value.hasUnreadNotifications))
|
||||
? Assets.svg.bellNew(context)
|
||||
: Assets.svg.bell,
|
||||
width: 20,
|
||||
height: 20,
|
||||
color: ref.watch(notificationsProvider
|
||||
.select((value) => value.hasUnreadNotifications))
|
||||
? null
|
||||
: DesktopMenuItemId.notifications ==
|
||||
ref.watch(currentDesktopMenuItemProvider.state).state
|
||||
? Theme.of(context).extension<StackColors>()!.accentColorDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark
|
||||
.withOpacity(0.8),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopMenuItem<T> extends ConsumerStatefulWidget {
|
||||
const DesktopMenuItem({
|
||||
Key? key,
|
||||
|
|
|
@ -6,7 +6,6 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart';
|
||||
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
|
@ -65,14 +64,8 @@ class _DesktopDeleteWalletDialog
|
|||
.verifyPassphrase(passwordController.text);
|
||||
|
||||
if (verified) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
|
||||
final words = await ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(widget.walletId)
|
||||
.mnemonic;
|
||||
|
||||
if (mounted) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
Navigator.of(context).pop();
|
||||
|
||||
unawaited(
|
||||
|
@ -83,17 +76,19 @@ class _DesktopDeleteWalletDialog
|
|||
);
|
||||
}
|
||||
} else {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
if (mounted) {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
await Future<void>.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Invalid passphrase!",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Invalid passphrase!",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,7 +152,9 @@ class _DesktopDeleteWalletDialog
|
|||
key: const Key("desktopDeleteWalletPasswordFieldKey"),
|
||||
focusNode: passwordFocusNode,
|
||||
controller: passwordController,
|
||||
style: STextStyles.field(context),
|
||||
style: STextStyles.desktopTextMedium(context).copyWith(
|
||||
height: 2,
|
||||
),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
|
@ -240,7 +237,7 @@ class _DesktopDeleteWalletDialog
|
|||
onPressed: _continueEnabled
|
||||
? () async {
|
||||
// add loading indicator
|
||||
enterPassphrase();
|
||||
await enterPassphrase();
|
||||
}
|
||||
: null,
|
||||
),
|
||||
|
|
|
@ -5,10 +5,19 @@ final validContactStateProvider =
|
|||
StateProvider.autoDispose.family<bool, List<int>>((ref, ids) {
|
||||
bool isValid = true;
|
||||
|
||||
bool hasAtLeastOneValid = false;
|
||||
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
isValid = isValid &&
|
||||
ref.watch(
|
||||
addressEntryDataProvider(ids[i]).select((value) => value.isValid));
|
||||
final _valid = ref.watch(
|
||||
addressEntryDataProvider(ids[i]).select((value) => value.isValid));
|
||||
|
||||
final _isEmpty = ref.watch(
|
||||
addressEntryDataProvider(ids[i]).select((value) => value.isEmpty));
|
||||
|
||||
isValid = isValid && (_valid || _isEmpty);
|
||||
if (_valid) {
|
||||
hasAtLeastOneValid = true;
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
return isValid && hasAtLeastOneValid;
|
||||
});
|
||||
|
|
|
@ -85,6 +85,7 @@ import 'package:stackwallet/pages/wallet_view/transaction_views/transaction_sear
|
|||
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
|
||||
import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/create_password/create_password_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/forgot_password_desktop_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/address_book_view/desktop_address_book.dart';
|
||||
|
@ -96,12 +97,14 @@ import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_vie
|
|||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_attention_delete_wallet.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_delete_wallet_dialog.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/currency_settings/currency_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/nodes_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/security_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/settings_menu.dart';
|
||||
|
@ -115,9 +118,6 @@ import 'package:stackwallet/utilities/enums/add_wallet_type_enum.dart';
|
|||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import 'pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/unlock_wallet_keys_desktop.dart';
|
||||
import 'pages_desktop_specific/home/settings_menu/language_settings/language_settings.dart';
|
||||
|
||||
class RouteGenerator {
|
||||
static const bool useMaterialPageRoute = true;
|
||||
|
||||
|
@ -1029,6 +1029,12 @@ class RouteGenerator {
|
|||
builder: (_) => const DesktopExchangeView(),
|
||||
settings: RouteSettings(name: settings.name));
|
||||
|
||||
case DesktopAllTradesView.routeName:
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => const DesktopAllTradesView(),
|
||||
settings: RouteSettings(name: settings.name));
|
||||
|
||||
case DesktopSettingsView.routeName:
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
|
|
|
@ -50,6 +50,9 @@ class AddressBookService extends ChangeNotifier {
|
|||
}
|
||||
|
||||
bool matches(String term, Contact contact) {
|
||||
if (term.isEmpty) {
|
||||
return true;
|
||||
}
|
||||
final text = term.toLowerCase();
|
||||
if (contact.name.toLowerCase().contains(text)) {
|
||||
return true;
|
||||
|
|
|
@ -11,6 +11,16 @@ class TradesService extends ChangeNotifier {
|
|||
return list;
|
||||
}
|
||||
|
||||
Trade? get(String tradeId) {
|
||||
try {
|
||||
return DB.instance
|
||||
.values<Trade>(boxName: DB.boxNameTradesV2)
|
||||
.firstWhere((e) => e.tradeId == tradeId);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> add({
|
||||
required Trade trade,
|
||||
required bool shouldNotifyListeners,
|
||||
|
|
|
@ -3,6 +3,10 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/providers/ui/color_theme_provider.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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/rounded_container.dart';
|
||||
|
||||
class BlueTextButton extends ConsumerStatefulWidget {
|
||||
const BlueTextButton({
|
||||
|
@ -28,6 +32,8 @@ class _BlueTextButtonState extends ConsumerState<BlueTextButton>
|
|||
Animation<dynamic>? animation;
|
||||
late Color color;
|
||||
|
||||
bool _hovering = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.enabled) {
|
||||
|
@ -65,25 +71,47 @@ class _BlueTextButtonState extends ConsumerState<BlueTextButton>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
text: widget.text,
|
||||
style: widget.textSize == null
|
||||
? STextStyles.link2(context).copyWith(
|
||||
color: color,
|
||||
)
|
||||
: STextStyles.link2(context).copyWith(
|
||||
color: color,
|
||||
fontSize: widget.textSize,
|
||||
),
|
||||
recognizer: widget.enabled
|
||||
? (TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
widget.onTap?.call();
|
||||
controller?.forward().then((value) => controller?.reverse());
|
||||
})
|
||||
: null,
|
||||
return ConditionalParent(
|
||||
condition: Util.isDesktop,
|
||||
builder: (child) {
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) => setState(() => _hovering = true),
|
||||
onExit: (_) => setState(() => _hovering = false),
|
||||
child: GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: RoundedContainer(
|
||||
radiusMultiplier: 20,
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.highlight
|
||||
.withOpacity(_hovering ? 0.3 : 0),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
text: widget.text,
|
||||
style: widget.textSize == null
|
||||
? STextStyles.link2(context).copyWith(
|
||||
color: color,
|
||||
)
|
||||
: STextStyles.link2(context).copyWith(
|
||||
color: color,
|
||||
fontSize: widget.textSize,
|
||||
),
|
||||
recognizer: widget.enabled
|
||||
? (TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
widget.onTap?.call();
|
||||
controller?.forward().then((value) => controller?.reverse());
|
||||
})
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class DesktopDialog extends StatelessWidget {
|
|||
|
||||
final Widget? child;
|
||||
final double maxWidth;
|
||||
final double maxHeight;
|
||||
final double? maxHeight;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -22,7 +22,7 @@ class DesktopDialog extends StatelessWidget {
|
|||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
maxHeight: maxHeight ?? MediaQuery.of(context).size.height - 64,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
|
|
311
lib/widgets/toggle.dart
Normal file
311
lib/widgets/toggle.dart
Normal file
|
@ -0,0 +1,311 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
|
||||
class Toggle extends StatefulWidget {
|
||||
const Toggle({
|
||||
Key? key,
|
||||
this.onIcon,
|
||||
this.onText,
|
||||
this.offIcon,
|
||||
this.offText,
|
||||
this.onValueChanged,
|
||||
required this.isOn,
|
||||
// this.enabled = true,
|
||||
this.controller,
|
||||
required this.onColor,
|
||||
required this.offColor,
|
||||
this.decoration,
|
||||
}) : super(key: key);
|
||||
|
||||
final String? onIcon;
|
||||
final String? onText;
|
||||
final String? offIcon;
|
||||
final String? offText;
|
||||
final void Function(bool)? onValueChanged;
|
||||
final bool isOn;
|
||||
// final bool enabled;
|
||||
final DSBController? controller;
|
||||
|
||||
final Color onColor;
|
||||
final Color offColor;
|
||||
final BoxDecoration? decoration;
|
||||
|
||||
@override
|
||||
ToggleState createState() => ToggleState();
|
||||
}
|
||||
|
||||
class ToggleState extends State<Toggle> {
|
||||
late final BoxDecoration? decoration;
|
||||
late final Color onColor;
|
||||
late final Color offColor;
|
||||
late final DSBController? controller;
|
||||
|
||||
final bool isDesktop = Util.isDesktop;
|
||||
|
||||
late bool _isOn;
|
||||
bool get isOn => _isOn;
|
||||
|
||||
// late bool _enabled;
|
||||
|
||||
late ValueNotifier<double> valueListener;
|
||||
|
||||
final tapAnimationDuration = const Duration(milliseconds: 150);
|
||||
bool _isDragging = false;
|
||||
|
||||
// Color _colorBG(bool isOn, double alpha) {
|
||||
// return Color.alphaBlend(
|
||||
// onColor.withOpacity(alpha),
|
||||
// offColor,
|
||||
// );
|
||||
// }
|
||||
|
||||
// Color _colorFG(bool isOn, double alpha) {
|
||||
// return Color.alphaBlend(
|
||||
// onColor.withOpacity(alpha),
|
||||
// offColor,
|
||||
// );
|
||||
// }
|
||||
|
||||
@override
|
||||
initState() {
|
||||
onColor = widget.onColor;
|
||||
offColor = widget.offColor;
|
||||
decoration = widget.decoration;
|
||||
controller = widget.controller;
|
||||
_isOn = widget.isOn;
|
||||
// _enabled = widget.enabled;
|
||||
valueListener = _isOn ? ValueNotifier(1.0) : ValueNotifier(0.0);
|
||||
|
||||
widget.controller?.activate = () {
|
||||
_isOn = !_isOn;
|
||||
// widget.onValueChanged?.call(_isOn);
|
||||
valueListener.value = _isOn ? 1.0 : 0.0;
|
||||
};
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
valueListener.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_isOn = !_isOn;
|
||||
widget.onValueChanged?.call(_isOn);
|
||||
valueListener.value = _isOn ? 1.0 : 0.0;
|
||||
setState(() {});
|
||||
},
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraint) {
|
||||
return Stack(
|
||||
children: [
|
||||
AnimatedBuilder(
|
||||
animation: valueListener,
|
||||
builder: (context, child) {
|
||||
return AnimatedContainer(
|
||||
duration: tapAnimationDuration,
|
||||
height: constraint.maxHeight,
|
||||
width: constraint.maxWidth,
|
||||
decoration: decoration?.copyWith(
|
||||
color: offColor,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final handle = GestureDetector(
|
||||
key: const Key("draggableSwitchButtonSwitch"),
|
||||
onHorizontalDragStart: (_) => _isDragging = true,
|
||||
onHorizontalDragUpdate: (details) {
|
||||
valueListener.value = (valueListener.value +
|
||||
details.delta.dx / constraint.maxWidth)
|
||||
.clamp(0.0, 1.0);
|
||||
},
|
||||
onHorizontalDragEnd: (details) {
|
||||
bool oldValue = _isOn;
|
||||
if (valueListener.value > 0.5) {
|
||||
valueListener.value = 1.0;
|
||||
_isOn = true;
|
||||
} else {
|
||||
valueListener.value = 0.0;
|
||||
_isOn = false;
|
||||
}
|
||||
if (_isOn != oldValue) {
|
||||
widget.onValueChanged?.call(_isOn);
|
||||
controller?.isOn = _isOn;
|
||||
setState(() {});
|
||||
}
|
||||
_isDragging = false;
|
||||
},
|
||||
child: AnimatedBuilder(
|
||||
animation: valueListener,
|
||||
builder: (context, child) {
|
||||
return AnimatedContainer(
|
||||
duration: tapAnimationDuration,
|
||||
height: constraint.maxHeight,
|
||||
width: constraint.maxWidth / 2,
|
||||
decoration: decoration?.copyWith(
|
||||
color:
|
||||
onColor, //_colorFG(_isOn, valueListener.value),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
return AnimatedBuilder(
|
||||
animation: valueListener,
|
||||
builder: (context, child) {
|
||||
return AnimatedAlign(
|
||||
duration:
|
||||
_isDragging ? Duration.zero : tapAnimationDuration,
|
||||
alignment: Alignment(valueListener.value * 2 - 1, 0.5),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: handle,
|
||||
);
|
||||
},
|
||||
),
|
||||
IgnorePointer(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: constraint.maxWidth / 2,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
widget.onIcon ?? "",
|
||||
width: 12,
|
||||
height: 14,
|
||||
color: isDesktop
|
||||
? !_isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary
|
||||
: !_isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text(
|
||||
widget.onText ?? "",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: !_isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
)
|
||||
: STextStyles.smallMed12(context).copyWith(
|
||||
color: !_isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: constraint.maxWidth / 2,
|
||||
child: Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
widget.offIcon ?? "",
|
||||
width: 12,
|
||||
height: 14,
|
||||
color: isDesktop
|
||||
? _isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary
|
||||
: _isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text(
|
||||
widget.offText ?? "",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: _isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorBlue
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.buttonTextSecondary,
|
||||
)
|
||||
: STextStyles.smallMed12(context).copyWith(
|
||||
color: _isOn
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark
|
||||
: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DSBController {
|
||||
VoidCallback? activate;
|
||||
bool? isOn;
|
||||
}
|
Loading…
Reference in a new issue