Merge pull request #853 from cypherstack/testing_julian

various tweaks and fixes
This commit is contained in:
julian-CStack 2024-05-07 16:37:34 -06:00 committed by GitHub
commit ec084d1a9e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 710 additions and 628 deletions

View file

@ -11,6 +11,7 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
@ -134,9 +135,9 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
_coins.remove(Coin.wownero);
}
// Remove Solana from the list of coins based on our frostEnabled preference.
if (!ref.read(prefsChangeNotifierProvider).solanaEnabled) {
_coins.remove(Coin.solana);
if (Util.isDesktop && !kDebugMode) {
_coins.remove(Coin.bitcoinFrost);
_coins.remove(Coin.bitcoinFrostTestNet);
}
coinEntities.addAll(_coins.map((e) => CoinEntity(e)));

View file

@ -11,9 +11,7 @@
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
import 'package:flutter_svg/svg.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/coin_image.dart';
import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/mobile_mnemonic_length_selector.dart';
import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/restore_options_view/sub_widgets/restore_from_date_picker.dart';
@ -24,7 +22,6 @@ import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/sub_widge
import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/ui/verify_recovery_phrase/mnemonic_word_count_state_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -33,6 +30,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/date_picker/date_picker.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/expandable.dart';
@ -42,10 +40,10 @@ import 'package:tuple/tuple.dart';
class RestoreOptionsView extends ConsumerStatefulWidget {
const RestoreOptionsView({
Key? key,
super.key,
required this.walletName,
required this.coin,
}) : super(key: key);
});
static const routeName = "/restoreOptions";
@ -68,7 +66,6 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
final bool _nextEnabled = true;
DateTime _restoreFromDate = DateTime.fromMillisecondsSinceEpoch(0);
late final Color baseColor;
bool hidePassword = true;
bool _expandedAdavnced = false;
@ -77,7 +74,6 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
@override
void initState() {
baseColor = ref.read(themeProvider.state).state.textSubtitle2;
walletName = widget.walletName;
coin = widget.coin;
isDesktop = Util.isDesktop;
@ -99,52 +95,6 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
super.dispose();
}
MaterialRoundedDatePickerStyle _buildDatePickerStyle() {
return MaterialRoundedDatePickerStyle(
paddingMonthHeader: const EdgeInsets.only(top: 11),
colorArrowNext: Theme.of(context).extension<StackColors>()!.textSubtitle1,
colorArrowPrevious:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
textStyleButtonNegative: STextStyles.datePicker600(context).copyWith(
color: baseColor,
),
textStyleButtonPositive: STextStyles.datePicker600(context).copyWith(
color: baseColor,
),
textStyleCurrentDayOnCalendar: STextStyles.datePicker400(context),
textStyleDayHeader: STextStyles.datePicker600(context),
textStyleDayOnCalendar: STextStyles.datePicker400(context).copyWith(
color: baseColor,
),
textStyleDayOnCalendarDisabled:
STextStyles.datePicker400(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle3,
),
textStyleDayOnCalendarSelected:
STextStyles.datePicker400(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.popupBG,
),
textStyleMonthYearHeader: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
textStyleYearButton: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textWhite,
),
textStyleButtonAction: GoogleFonts.inter(),
);
}
MaterialRoundedYearPickerStyle _buildYearPickerStyle() {
return MaterialRoundedYearPickerStyle(
textStyleYear: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
),
textStyleYearSelected: STextStyles.datePicker600(context).copyWith(
fontSize: 18,
),
);
}
Future<void> nextPressed() async {
if (!isDesktop) {
// hide keyboard if has focus
@ -169,67 +119,23 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
}
Future<void> chooseDate() async {
final height = MediaQuery.of(context).size.height;
final fetchedColor =
Theme.of(context).extension<StackColors>()!.accentColorDark;
// check and hide keyboard
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 125));
}
final date = await showRoundedDatePicker(
context: context,
initialDate: DateTime.now(),
height: height / 3.0,
theme: ThemeData(
primarySwatch: Util.createMaterialColor(fetchedColor),
),
//TODO pick a better initial date
// 2007 chosen as that is just before bitcoin launched
firstDate: DateTime(2007),
lastDate: DateTime.now(),
borderRadius: Constants.size.circularBorderRadius * 2,
textPositiveButton: "SELECT",
styleDatePicker: _buildDatePickerStyle(),
styleYearPicker: _buildYearPickerStyle(),
);
if (date != null) {
_restoreFromDate = date;
_dateController.text = Format.formatDate(date);
if (mounted) {
final date = await showSWDatePicker(context);
if (date != null) {
_restoreFromDate = date;
_dateController.text = Format.formatDate(date);
}
}
}
Future<void> chooseDesktopDate() async {
final height = MediaQuery.of(context).size.height;
final fetchedColor =
Theme.of(context).extension<StackColors>()!.accentColorDark;
// check and hide keyboard
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 125));
}
final date = await showRoundedDatePicker(
context: context,
initialDate: DateTime.now(),
height: height / 3.0,
theme: ThemeData(
primarySwatch: Util.createMaterialColor(fetchedColor),
),
//TODO pick a better initial date
// 2007 chosen as that is just before bitcoin launched
firstDate: DateTime(2007),
lastDate: DateTime.now(),
borderRadius: Constants.size.circularBorderRadius * 2,
textPositiveButton: "SELECT",
styleDatePicker: _buildDatePickerStyle(),
styleYearPicker: _buildYearPickerStyle(),
);
final date = await showSWDatePicker(context);
if (date != null) {
_restoreFromDate = date;
_dateController.text = Format.formatDate(date);

View file

@ -29,6 +29,7 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -39,10 +40,10 @@ final exchangeFromAddressBookAddressStateProvider =
class ContactPopUp extends ConsumerWidget {
const ContactPopUp({
Key? key,
super.key,
required this.contactId,
this.clipboard = const ClipboardWrapper(),
}) : super(key: key);
});
final String contactId;
final ClipboardInterface clipboard;
@ -384,13 +385,18 @@ class ContactPopUp extends ConsumerWidget {
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
padding:
const EdgeInsets.all(4),
padding: EdgeInsets.all(
Util.isDesktop ? 4 : 6,
),
child: SvgPicture.asset(
Assets
.svg.circleArrowUpRight,
width: 12,
height: 12,
width: Util.isDesktop
? 12
: 16,
height: Util.isDesktop
? 12
: 16,
color: Theme.of(context)
.extension<
StackColors>()!

View file

@ -10,7 +10,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/themes/theme_providers.dart';
@ -21,6 +20,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/date_picker/date_picker.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
@ -69,8 +69,8 @@ final ordinalFilterProvider = StateProvider<OrdinalFilter?>((_) => null);
class OrdinalsFilterView extends ConsumerStatefulWidget {
const OrdinalsFilterView({
Key? key,
}) : super(key: key);
super.key,
});
static const String routeName = "/ordinalsFilterView";
@ -146,56 +146,6 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
DateTime? _selectedFromDate = DateTime(2007);
DateTime? _selectedToDate = DateTime.now();
MaterialRoundedDatePickerStyle _buildDatePickerStyle() {
return MaterialRoundedDatePickerStyle(
backgroundPicker: Theme.of(context).extension<StackColors>()!.popupBG,
// backgroundHeader: Theme.of(context).extension<StackColors>()!.textSubtitle2,
paddingMonthHeader: const EdgeInsets.only(top: 11),
colorArrowNext: Theme.of(context).extension<StackColors>()!.textSubtitle1,
colorArrowPrevious:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
textStyleButtonNegative: STextStyles.datePicker600(context).copyWith(
color: baseColor,
),
textStyleButtonPositive: STextStyles.datePicker600(context).copyWith(
color: baseColor,
),
textStyleCurrentDayOnCalendar: STextStyles.datePicker400(context),
textStyleDayHeader: STextStyles.datePicker600(context),
textStyleDayOnCalendar: STextStyles.datePicker400(context).copyWith(
color: baseColor,
),
textStyleDayOnCalendarDisabled:
STextStyles.datePicker400(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle3,
),
textStyleDayOnCalendarSelected:
STextStyles.datePicker400(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textWhite,
),
textStyleMonthYearHeader: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
textStyleYearButton: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textWhite,
),
// textStyleButtonAction: GoogleFonts.inter(),
);
}
MaterialRoundedYearPickerStyle _buildYearPickerStyle() {
return MaterialRoundedYearPickerStyle(
backgroundPicker: Theme.of(context).extension<StackColors>()!.popupBG,
textStyleYear: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
fontSize: 16,
),
textStyleYearSelected: STextStyles.datePicker600(context).copyWith(
fontSize: 18,
),
);
}
Widget _buildDateRangePicker() {
const middleSeparatorPadding = 2.0;
const middleSeparatorWidth = 12.0;
@ -216,9 +166,6 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
child: GestureDetector(
key: const Key("OrdinalsViewFromDatePickerKey"),
onTap: () async {
final color =
Theme.of(context).extension<StackColors>()!.accentColorDark;
final height = MediaQuery.of(context).size.height;
// check and hide keyboard
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
@ -226,28 +173,7 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
}
if (mounted) {
final date = await showRoundedDatePicker(
// This doesn't change statusbar color...
// background: CFColors.starryNight.withOpacity(0.8),
context: context,
initialDate: DateTime.now(),
height: height * 0.5,
theme: ThemeData(
primarySwatch: Util.createMaterialColor(
color,
),
),
//TODO pick a better initial date
// 2007 chosen as that is just before bitcoin launched
firstDate: DateTime(2007),
lastDate: DateTime.now(),
borderRadius: Constants.size.circularBorderRadius * 2,
textPositiveButton: "SELECT",
styleDatePicker: _buildDatePickerStyle(),
styleYearPicker: _buildYearPickerStyle(),
);
final date = await showSWDatePicker(context);
if (date != null) {
_selectedFromDate = date;
@ -330,9 +256,6 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
child: GestureDetector(
key: const Key("OrdinalsViewToDatePickerKey"),
onTap: () async {
final color =
Theme.of(context).extension<StackColors>()!.accentColorDark;
final height = MediaQuery.of(context).size.height;
// check and hide keyboard
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
@ -340,28 +263,7 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
}
if (mounted) {
final date = await showRoundedDatePicker(
// This doesn't change statusbar color...
// background: CFColors.starryNight.withOpacity(0.8),
context: context,
height: height * 0.5,
theme: ThemeData(
primarySwatch: Util.createMaterialColor(
color,
),
),
//TODO pick a better initial date
// 2007 chosen as that is just before bitcoin launched
initialDate: DateTime.now(),
firstDate: DateTime(2007),
lastDate: DateTime.now(),
borderRadius: Constants.size.circularBorderRadius * 2,
textPositiveButton: "SELECT",
styleDatePicker: _buildDatePickerStyle(),
styleYearPicker: _buildYearPickerStyle(),
);
final date = await showSWDatePicker(context);
if (date != null) {
_selectedToDate = date;
@ -467,7 +369,7 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
if (context.mounted) {
Navigator.of(context).pop();
}
},
@ -840,7 +742,7 @@ class _OrdinalsFilterViewState extends ConsumerState<OrdinalsFilterView> {
);
}
}
if (mounted) {
if (context.mounted) {
Navigator.of(context).pop();
}
},

View file

@ -10,7 +10,6 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -277,36 +276,6 @@ class HiddenSettings extends StatelessWidget {
const SizedBox(
height: 12,
),
Consumer(
builder: (_, ref, __) {
return GestureDetector(
onTap: () async {
ref
.read(prefsChangeNotifierProvider)
.solanaEnabled =
!(ref
.read(prefsChangeNotifierProvider)
.solanaEnabled);
if (kDebugMode) {
print(
"Solana enabled: ${ref.read(prefsChangeNotifierProvider).solanaEnabled}");
}
},
child: RoundedWhiteContainer(
child: Text(
"Toggle Solana",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
);
},
),
const SizedBox(
height: 12,
),
Consumer(
builder: (_, ref, __) {
return GestureDetector(

View file

@ -11,7 +11,6 @@
import 'package:decimal/decimal.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/transaction_filter.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
@ -29,6 +28,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/date_picker/date_picker.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
@ -40,9 +40,9 @@ import 'package:stackwallet/widgets/textfield_icon_button.dart';
class TransactionSearchFilterView extends ConsumerStatefulWidget {
const TransactionSearchFilterView({
Key? key,
super.key,
required this.coin,
}) : super(key: key);
});
static const String routeName = "/transactionSearchFilter";
@ -137,56 +137,6 @@ class _TransactionSearchViewState
DateTime? _selectedFromDate = DateTime(2007);
DateTime? _selectedToDate = DateTime.now();
MaterialRoundedDatePickerStyle _buildDatePickerStyle() {
return MaterialRoundedDatePickerStyle(
backgroundPicker: Theme.of(context).extension<StackColors>()!.popupBG,
// backgroundHeader: Theme.of(context).extension<StackColors>()!.textSubtitle2,
paddingMonthHeader: const EdgeInsets.only(top: 11),
colorArrowNext: Theme.of(context).extension<StackColors>()!.textSubtitle1,
colorArrowPrevious:
Theme.of(context).extension<StackColors>()!.textSubtitle1,
textStyleButtonNegative: STextStyles.datePicker600(context).copyWith(
color: baseColor,
),
textStyleButtonPositive: STextStyles.datePicker600(context).copyWith(
color: baseColor,
),
textStyleCurrentDayOnCalendar: STextStyles.datePicker400(context),
textStyleDayHeader: STextStyles.datePicker600(context),
textStyleDayOnCalendar: STextStyles.datePicker400(context).copyWith(
color: baseColor,
),
textStyleDayOnCalendarDisabled:
STextStyles.datePicker400(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle3,
),
textStyleDayOnCalendarSelected:
STextStyles.datePicker400(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textWhite,
),
textStyleMonthYearHeader: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle1,
),
textStyleYearButton: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textWhite,
),
// textStyleButtonAction: GoogleFonts.inter(),
);
}
MaterialRoundedYearPickerStyle _buildYearPickerStyle() {
return MaterialRoundedYearPickerStyle(
backgroundPicker: Theme.of(context).extension<StackColors>()!.popupBG,
textStyleYear: STextStyles.datePicker600(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
fontSize: 16,
),
textStyleYearSelected: STextStyles.datePicker600(context).copyWith(
fontSize: 18,
),
);
}
Widget _buildDateRangePicker() {
const middleSeparatorPadding = 2.0;
const middleSeparatorWidth = 12.0;
@ -207,58 +157,36 @@ class _TransactionSearchViewState
child: GestureDetector(
key: const Key("transactionSearchViewFromDatePickerKey"),
onTap: () async {
final color =
Theme.of(context).extension<StackColors>()!.accentColorDark;
final height = MediaQuery.of(context).size.height;
// check and hide keyboard
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 125));
}
final date = await showRoundedDatePicker(
// This doesn't change statusbar color...
// background: CFColors.starryNight.withOpacity(0.8),
context: context,
initialDate: DateTime.now(),
height: height * 0.5,
theme: ThemeData(
primarySwatch: Util.createMaterialColor(
color,
),
),
//TODO pick a better initial date
// 2007 chosen as that is just before bitcoin launched
firstDate: DateTime(2007),
lastDate: DateTime.now(),
borderRadius: Constants.size.circularBorderRadius * 2,
if (mounted) {
final date = await showSWDatePicker(context);
if (date != null) {
_selectedFromDate = date;
textPositiveButton: "SELECT",
styleDatePicker: _buildDatePickerStyle(),
styleYearPicker: _buildYearPickerStyle(),
);
if (date != null) {
_selectedFromDate = date;
// flag to adjust date so from date is always before to date
final flag = _selectedToDate != null &&
!_selectedFromDate!.isBefore(_selectedToDate!);
if (flag) {
_selectedToDate = DateTime.fromMillisecondsSinceEpoch(
_selectedFromDate!.millisecondsSinceEpoch);
}
setState(() {
// flag to adjust date so from date is always before to date
final flag = _selectedToDate != null &&
!_selectedFromDate!.isBefore(_selectedToDate!);
if (flag) {
_toDateString = _selectedToDate == null
? ""
: Format.formatDate(_selectedToDate!);
_selectedToDate = DateTime.fromMillisecondsSinceEpoch(
_selectedFromDate!.millisecondsSinceEpoch);
}
_fromDateString = _selectedFromDate == null
? ""
: Format.formatDate(_selectedFromDate!);
});
setState(() {
if (flag) {
_toDateString = _selectedToDate == null
? ""
: Format.formatDate(_selectedToDate!);
}
_fromDateString = _selectedFromDate == null
? ""
: Format.formatDate(_selectedFromDate!);
});
}
}
},
child: Container(
@ -319,58 +247,36 @@ class _TransactionSearchViewState
child: GestureDetector(
key: const Key("transactionSearchViewToDatePickerKey"),
onTap: () async {
final color =
Theme.of(context).extension<StackColors>()!.accentColorDark;
final height = MediaQuery.of(context).size.height;
// check and hide keyboard
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 125));
}
final date = await showRoundedDatePicker(
// This doesn't change statusbar color...
// background: CFColors.starryNight.withOpacity(0.8),
context: context,
height: height * 0.5,
theme: ThemeData(
primarySwatch: Util.createMaterialColor(
color,
),
),
//TODO pick a better initial date
// 2007 chosen as that is just before bitcoin launched
initialDate: DateTime.now(),
firstDate: DateTime(2007),
lastDate: DateTime.now(),
borderRadius: Constants.size.circularBorderRadius * 2,
if (mounted) {
final date = await showSWDatePicker(context);
if (date != null) {
_selectedToDate = date;
textPositiveButton: "SELECT",
styleDatePicker: _buildDatePickerStyle(),
styleYearPicker: _buildYearPickerStyle(),
);
if (date != null) {
_selectedToDate = date;
// flag to adjust date so from date is always before to date
final flag = _selectedFromDate != null &&
!_selectedToDate!.isAfter(_selectedFromDate!);
if (flag) {
_selectedFromDate = DateTime.fromMillisecondsSinceEpoch(
_selectedToDate!.millisecondsSinceEpoch);
}
setState(() {
// flag to adjust date so from date is always before to date
final flag = _selectedFromDate != null &&
!_selectedToDate!.isAfter(_selectedFromDate!);
if (flag) {
_fromDateString = _selectedFromDate == null
? ""
: Format.formatDate(_selectedFromDate!);
_selectedFromDate = DateTime.fromMillisecondsSinceEpoch(
_selectedToDate!.millisecondsSinceEpoch);
}
_toDateString = _selectedToDate == null
? ""
: Format.formatDate(_selectedToDate!);
});
setState(() {
if (flag) {
_fromDateString = _selectedFromDate == null
? ""
: Format.formatDate(_selectedFromDate!);
}
_toDateString = _selectedToDate == null
? ""
: Format.formatDate(_selectedToDate!);
});
}
}
},
child: Container(
@ -454,7 +360,7 @@ class _TransactionSearchViewState
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
if (context.mounted) {
Navigator.of(context).pop();
}
},
@ -908,7 +814,7 @@ class _TransactionSearchViewState
);
}
}
if (mounted) {
if (context.mounted) {
Navigator.of(context).pop();
}
},

View file

@ -26,9 +26,9 @@ import 'package:stackwallet/widgets/stack_text_field.dart';
class DesktopAuthSend extends ConsumerStatefulWidget {
const DesktopAuthSend({
Key? key,
super.key,
required this.coin,
}) : super(key: key);
});
final Coin coin;
@ -43,11 +43,52 @@ class _DesktopAuthSendState extends ConsumerState<DesktopAuthSend> {
bool hidePassword = true;
bool _confirmEnabled = false;
bool _lock = false;
Future<bool> verifyPassphrase() async {
return await ref
.read(storageCryptoHandlerProvider)
.verifyPassphrase(passwordController.text);
Future<void> _confirmPressed() async {
if (_lock) {
return;
}
_lock = true;
try {
unawaited(
showDialog<void>(
context: context,
builder: (context) => const Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
LoadingIndicator(
width: 200,
height: 200,
),
],
),
),
);
await Future<void>.delayed(const Duration(seconds: 1));
final passwordIsValid = await ref
.read(storageCryptoHandlerProvider)
.verifyPassphrase(passwordController.text);
if (mounted) {
Navigator.of(context).pop();
Navigator.of(
context,
rootNavigator: true,
).pop(passwordIsValid);
await Future<void>.delayed(
const Duration(
milliseconds: 100,
),
);
}
} finally {
_lock = false;
}
}
@override
@ -108,6 +149,12 @@ class _DesktopAuthSendState extends ConsumerState<DesktopAuthSend> {
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
autofocus: true,
onSubmitted: (_) {
if (_confirmEnabled) {
_confirmPressed();
}
},
decoration: standardInputDecoration(
"Enter password",
passwordFocusNode,
@ -173,38 +220,7 @@ class _DesktopAuthSendState extends ConsumerState<DesktopAuthSend> {
enabled: _confirmEnabled,
label: "Confirm",
buttonHeight: ButtonHeight.l,
onPressed: () async {
unawaited(
showDialog<void>(
context: context,
builder: (context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
LoadingIndicator(
width: 200,
height: 200,
),
],
),
),
);
await Future<void>.delayed(const Duration(seconds: 1));
final passwordIsValid = await verifyPassphrase();
if (mounted) {
Navigator.of(context).pop();
Navigator.of(
context,
rootNavigator: true,
).pop(passwordIsValid);
await Future<void>.delayed(const Duration(
milliseconds: 100,
));
}
},
onPressed: _confirmPressed,
),
),
],

View file

@ -686,7 +686,7 @@ abstract class EthereumAPI {
try {
final response = await client.get(
url: Uri.parse(
"$stackBaseServer/abis?addrs=$contractAddress&verbose=true",
"$stackBaseServer/abis?addrs=$contractAddress",
),
proxyInfo: Prefs.instance.useTor
? TorService.sharedInstance.getProxyInfo()

View file

@ -8,6 +8,8 @@
*
*/
import 'dart:async';
import 'package:flutter_libmonero/monero/monero.dart';
import 'package:flutter_libmonero/wownero/wownero.dart';
import 'package:isar/isar.dart';
@ -16,7 +18,6 @@ import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/notifications_service.dart';
import 'package:stackwallet/services/trade_sent_from_stack_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/sync_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
@ -134,6 +135,11 @@ class Wallets {
}
Future<void> load(Prefs prefs, MainDB mainDB) async {
// return await _loadV1(prefs, mainDB);
return await _loadV2(prefs, mainDB);
}
Future<void> _loadV1(Prefs prefs, MainDB mainDB) async {
if (hasLoaded) {
return;
}
@ -156,10 +162,11 @@ class Wallets {
return;
}
List<Future<void>> walletInitFutures = [];
List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = [];
final List<Future<void>> walletInitFutures = [];
final List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly =
[];
List<String> walletIdsToEnableAutoSync = [];
final List<String> walletIdsToEnableAutoSync = [];
bool shouldAutoSyncAll = false;
switch (prefs.syncType) {
case SyncingType.currentWalletOnly:
@ -185,8 +192,8 @@ class Wallets {
if (isVerified) {
// TODO: integrate this into the new wallets somehow?
// requires some thinking
final txTracker =
TransactionNotificationTracker(walletId: walletInfo.walletId);
// final txTracker =
// TransactionNotificationTracker(walletId: walletInfo.walletId);
final wallet = await Wallet.load(
walletId: walletInfo.walletId,
@ -233,15 +240,134 @@ class Wallets {
}
}
Future<void> _loadV2(Prefs prefs, MainDB mainDB) async {
if (hasLoaded) {
return;
}
hasLoaded = true;
// clear out any wallet hive boxes where the wallet was deleted in previous app run
for (final walletId in DB.instance
.values<String>(boxName: DB.boxNameWalletsToDeleteOnStart)) {
await mainDB.isar.writeTxn(() async => await mainDB.isar.walletInfo
.where()
.walletIdEqualTo(walletId)
.deleteAll());
}
// clear list
await DB.instance
.deleteAll<String>(boxName: DB.boxNameWalletsToDeleteOnStart);
final walletInfoList = await mainDB.isar.walletInfo.where().findAll();
if (walletInfoList.isEmpty) {
return;
}
final List<Future<String>> walletIDInitFutures = [];
final List<Future<void>> deleteFutures = [];
final List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly =
[];
final List<String> walletIdsToEnableAutoSync = [];
bool shouldAutoSyncAll = false;
switch (prefs.syncType) {
case SyncingType.currentWalletOnly:
// do nothing as this will be set when going into a wallet from the main screen
break;
case SyncingType.selectedWalletsAtStartup:
walletIdsToEnableAutoSync.addAll(prefs.walletIdsSyncOnStartup);
break;
case SyncingType.allWalletsOnStartup:
shouldAutoSyncAll = true;
break;
}
for (final walletInfo in walletInfoList) {
try {
final isVerified = await walletInfo.isMnemonicVerified(mainDB.isar);
Logging.instance.log(
"LOADING WALLET: ${walletInfo.name}:${walletInfo.walletId} "
"IS VERIFIED: $isVerified",
level: LogLevel.Info,
);
if (isVerified) {
// TODO: integrate this into the new wallets somehow?
// requires some thinking
// final txTracker =
// TransactionNotificationTracker(walletId: walletInfo.walletId);
final walletIdCompleter = Completer<String>();
walletIDInitFutures.add(walletIdCompleter.future);
await Wallet.load(
walletId: walletInfo.walletId,
mainDB: mainDB,
secureStorageInterface: nodeService.secureStorageInterface,
nodeService: nodeService,
prefs: prefs,
).then((wallet) {
if (wallet is CwBasedInterface) {
// walletsToInitLinearly.add(Tuple2(manager, shouldSetAutoSync));
walletIdCompleter.complete("dummy_ignore");
} else {
walletIdCompleter.complete(wallet.walletId);
}
_wallets[wallet.walletId] = wallet;
});
} else {
// wallet creation was not completed by user so we remove it completely
deleteFutures.add(_deleteWallet(walletInfo.walletId));
}
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Fatal);
continue;
}
}
final asyncWalletIds = await Future.wait(walletIDInitFutures);
asyncWalletIds.removeWhere((e) => e == "dummy_ignore");
final List<Future<void>> walletInitFutures = asyncWalletIds
.map(
(id) => _wallets[id]!.init().then(
(_) {
if (shouldAutoSyncAll || walletIdsToEnableAutoSync.contains(id)) {
_wallets[id]!.shouldAutoSync = true;
}
},
),
)
.toList();
if (walletInitFutures.isNotEmpty && walletsToInitLinearly.isNotEmpty) {
unawaited(Future.wait([
_initLinearly(walletsToInitLinearly),
...walletInitFutures,
]));
} else if (walletInitFutures.isNotEmpty) {
unawaited(Future.wait(walletInitFutures));
} else if (walletsToInitLinearly.isNotEmpty) {
unawaited(_initLinearly(walletsToInitLinearly));
}
// finally await any deletions that haven't completed yet
await Future.wait(deleteFutures);
}
Future<void> loadAfterStackRestore(
Prefs prefs,
List<Wallet> wallets,
bool isDesktop,
) async {
List<Future<void>> walletInitFutures = [];
List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly = [];
final List<Future<void>> walletInitFutures = [];
final List<({Wallet wallet, bool shouldAutoSync})> walletsToInitLinearly =
[];
List<String> walletIdsToEnableAutoSync = [];
final List<String> walletIdsToEnableAutoSync = [];
bool shouldAutoSyncAll = false;
switch (prefs.syncType) {
case SyncingType.currentWalletOnly:

View file

@ -68,7 +68,6 @@ class Prefs extends ChangeNotifier {
await _setMaxDecimals();
_useTor = await _getUseTor();
_fusionServerInfo = await _getFusionServerInfo();
_solanaEnabled = await _getSolanaEnabled();
_initialized = true;
}
@ -1009,25 +1008,4 @@ class Prefs extends ChangeNotifier {
return actualMap;
}
// Solana
bool _solanaEnabled = false;
bool get solanaEnabled => _solanaEnabled;
set solanaEnabled(bool solanaEnabled) {
if (_solanaEnabled != solanaEnabled) {
DB.instance.put<dynamic>(
boxName: DB.boxNamePrefs, key: "solanaEnabled", value: solanaEnabled);
_solanaEnabled = solanaEnabled;
notifyListeners();
}
}
Future<bool> _getSolanaEnabled() async {
return await DB.instance.get<dynamic>(
boxName: DB.boxNamePrefs, key: "solanaEnabled") as bool? ??
false;
}
}

View file

@ -62,14 +62,15 @@ class FiroWallet extends Bip39HDWallet
@override
Future<void> updateTransactions() async {
List<Address> allAddressesOld = await fetchAddressesForElectrumXScan();
final List<Address> allAddressesOld =
await fetchAddressesForElectrumXScan();
Set<String> receivingAddresses = allAddressesOld
final Set<String> receivingAddresses = allAddressesOld
.where((e) => e.subType == AddressSubType.receiving)
.map((e) => convertAddressString(e.value))
.toSet();
Set<String> changeAddresses = allAddressesOld
final Set<String> changeAddresses = allAddressesOld
.where((e) => e.subType == AddressSubType.change)
.map((e) => convertAddressString(e.value))
.toSet();
@ -98,7 +99,7 @@ class FiroWallet extends Bip39HDWallet
}
}
List<Map<String, dynamic>> allTransactions = [];
final List<Map<String, dynamic>> allTransactions = [];
// some lelantus transactions aren't fetched via wallet addresses so they
// will never show as confirmed in the gui.
@ -177,7 +178,7 @@ class FiroWallet extends Bip39HDWallet
bool isMint = false;
bool isJMint = false;
bool isSparkMint = false;
bool isMasterNodePayment = false;
final bool isMasterNodePayment = false;
final bool isSparkSpend = txData["type"] == 9 && txData["version"] == 3;
final bool isMySpark = sparkTxids.contains(txData["txid"] as String);
@ -548,8 +549,12 @@ class FiroWallet extends Bip39HDWallet
}
@override
Future<({String? blockedReason, bool blocked, String? utxoLabel})>
checkBlockUTXO(
Future<
({
String? blockedReason,
bool blocked,
String? utxoLabel,
})> checkBlockUTXO(
Map<String, dynamic> jsonUTXO,
String? scriptPubKeyHex,
Map<String, dynamic>? jsonTX,
@ -557,30 +562,26 @@ class FiroWallet extends Bip39HDWallet
) async {
bool blocked = false;
String? blockedReason;
//
// if (jsonTX != null) {
// // check for bip47 notification
// final outputs = jsonTX["vout"] as List;
// for (final output in outputs) {
// List<String>? scriptChunks =
// (output['scriptPubKey']?['asm'] as String?)?.split(" ");
// if (scriptChunks?.length == 2 && scriptChunks?[0] == "OP_RETURN") {
// final blindedPaymentCode = scriptChunks![1];
// final bytes = blindedPaymentCode.toUint8ListFromHex;
//
// // https://en.bitcoin.it/wiki/BIP_0047#Sending
// if (bytes.length == 80 && bytes.first == 1) {
// blocked = true;
// blockedReason = "Paynym notification output. Incautious "
// "handling of outputs from notification transactions "
// "may cause unintended loss of privacy.";
// break;
// }
// }
// }
// }
//
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: null);
String? label;
if (jsonUTXO["value"] is int) {
// TODO: [prio=med] use special electrumx call to verify the 1000 Firo output is masternode
blocked = Amount.fromDecimal(
Decimal.fromInt(
1000, // 1000 firo output is a possible master node
),
fractionDigits: cryptoCurrency.fractionDigits,
).raw ==
BigInt.from(jsonUTXO["value"] as int);
if (blocked) {
blockedReason = "Possible masternode output. "
"Unlock and spend at your own risk.";
label = "Possible masternode";
}
}
return (blockedReason: blockedReason, blocked: blocked, utxoLabel: label);
}
@override
@ -632,9 +633,11 @@ class FiroWallet extends Bip39HDWallet
level: LogLevel.Info,
);
final canBatch = await serverCanBatch;
for (final type in cryptoCurrency.supportedDerivationPathTypes) {
receiveFutures.add(
serverCanBatch
canBatch
? checkGapsBatched(
txCountBatchSize,
root,
@ -656,7 +659,7 @@ class FiroWallet extends Bip39HDWallet
);
for (final type in cryptoCurrency.supportedDerivationPathTypes) {
changeFutures.add(
serverCanBatch
canBatch
? checkGapsBatched(
txCountBatchSize,
root,

View file

@ -92,16 +92,8 @@ class EthTokenWallet extends Wallet {
);
}
// String? mnemonicString = await ethWallet.getMnemonic();
//
// //Get private key for given mnemonic
// String privateKey = getPrivateKey(
// mnemonicString,
// (await ethWallet.getMnemonicPassphrase()),
// );
// _credentials = web3dart.EthPrivateKey.fromHex(privateKey);
try {
// try parse abi and extract transfer function
_deployedContract = web3dart.DeployedContract(
ContractAbiExtensions.fromJsonList(
jsonList: tokenContract.abi!,
@ -109,90 +101,51 @@ class EthTokenWallet extends Wallet {
),
contractAddress,
);
} catch (_) {
rethrow;
}
try {
_sendFunction = _deployedContract.function('transfer');
} catch (_) {
//====================================================================
// final list = List<Map<String, dynamic>>.from(
// jsonDecode(tokenContract.abi!) as List);
// final functionNames = list.map((e) => e["name"] as String);
//
// if (!functionNames.contains("balanceOf")) {
// list.add(
// {
// "encoding": "0x70a08231",
// "inputs": [
// {"name": "account", "type": "address"}
// ],
// "name": "balanceOf",
// "outputs": [
// {"name": "val_0", "type": "uint256"}
// ],
// "signature": "balanceOf(address)",
// "type": "function"
// },
// );
// }
//
// if (!functionNames.contains("transfer")) {
// list.add(
// {
// "encoding": "0xa9059cbb",
// "inputs": [
// {"name": "dst", "type": "address"},
// {"name": "rawAmount", "type": "uint256"}
// ],
// "name": "transfer",
// "outputs": [
// {"name": "val_0", "type": "bool"}
// ],
// "signature": "transfer(address,uint256)",
// "type": "function"
// },
// );
// }
//--------------------------------------------------------------------
//====================================================================
// function not found so likely a proxy so we need to fetch the impl
//====================================================================
// final updatedToken = tokenContract.copyWith(abi: jsonEncode(list));
// // Store updated contract
// final id = await MainDB.instance.putEthContract(updatedToken);
// _tokenContract = updatedToken..id = id;
//--------------------------------------------------------------------
final contractAddressResponse =
await EthereumAPI.getProxyTokenImplementationAddress(
contractAddress.hex);
if (contractAddressResponse.value != null) {
_tokenContract = await _updateTokenABI(
forContract: tokenContract,
usingContractAddress: contractAddressResponse.value!,
);
} else {
throw contractAddressResponse.exception!;
}
//====================================================================
}
try {
_deployedContract = web3dart.DeployedContract(
ContractAbiExtensions.fromJsonList(
jsonList: tokenContract.abi!,
name: tokenContract.name,
),
contractAddress,
// some failure so first try to make sure we have the latest abi
_tokenContract = await _updateTokenABI(
forContract: tokenContract,
usingContractAddress: contractAddress.hex,
);
} catch (_) {
rethrow;
}
_sendFunction = _deployedContract.function('transfer');
try {
// try again to parse abi and extract transfer function
_deployedContract = web3dart.DeployedContract(
ContractAbiExtensions.fromJsonList(
jsonList: tokenContract.abi!,
name: tokenContract.name,
),
contractAddress,
);
_sendFunction = _deployedContract.function('transfer');
} catch (_) {
// if it fails again we check if there is a proxy token impl and
// then try one last time to update and parse the abi
final contractAddressResponse =
await EthereumAPI.getProxyTokenImplementationAddress(
contractAddress.hex);
if (contractAddressResponse.value != null) {
_tokenContract = await _updateTokenABI(
forContract: tokenContract,
usingContractAddress: contractAddressResponse.value!,
);
} else {
throw contractAddressResponse.exception!;
}
_deployedContract = web3dart.DeployedContract(
ContractAbiExtensions.fromJsonList(
jsonList: tokenContract.abi!,
name: tokenContract.name,
),
contractAddress,
);
_sendFunction = _deployedContract.function('transfer');
}
}
} catch (e, s) {
Logging.instance.log(
"$runtimeType wallet failed init(): $e\n$s",

View file

@ -35,11 +35,20 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
static const _kServerBatchCutoffVersion = [1, 6];
List<int>? _serverVersion;
bool get serverCanBatch {
Future<bool> get serverCanBatch async {
// Firo server added batching without incrementing version number...
if (cryptoCurrency is Firo) {
return true;
}
try {
_serverVersion ??= _parseServerVersion((await electrumXClient
.getServerFeatures()
.timeout(const Duration(seconds: 2)))["server_version"] as String);
} catch (_) {
// ignore failure as it doesn't matter
}
if (_serverVersion != null && _serverVersion!.length > 2) {
if (_serverVersion![0] > _kServerBatchCutoffVersion[0]) {
return true;
@ -193,8 +202,8 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
.log('utxoObjectsToUse: $utxoObjectsToUse', level: LogLevel.Info);
// numberOfOutputs' length must always be equal to that of recipientsArray and recipientsAmtArray
List<String> recipientsArray = [recipientAddress];
List<int> recipientsAmtArray = [satoshiAmountToSend];
final List<String> recipientsArray = [recipientAddress];
final List<int> recipientsAmtArray = [satoshiAmountToSend];
// gather required signing data
final utxoSigningData = await fetchBuildTxData(utxoObjectsToUse);
@ -325,7 +334,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
feeForOneOutput + cryptoCurrency.dustLimit.raw.toInt()) {
// Here, we know that theoretically, we may be able to include another output(change) but we first need to
// factor in the value of this output in satoshis.
int changeOutputSize =
final int changeOutputSize =
satoshisBeingUsed - satoshiAmountToSend - feeForTwoOutputs;
// We check to see if the user can pay for the new transaction with 2 outputs instead of one. If they can and
// the second output's size > cryptoCurrency.dustLimit satoshis, we perform the mechanics required to properly generate and use a new
@ -370,7 +379,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
// make sure minimum fee is accurate if that is being used
if (txn.vSize! - feeBeingPaid == 1) {
int changeOutputSize =
final int changeOutputSize =
satoshisBeingUsed - satoshiAmountToSend - txn.vSize!;
feeBeingPaid =
satoshisBeingUsed - satoshiAmountToSend - changeOutputSize;
@ -526,7 +535,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
List<UTXO> utxosToUse,
) async {
// return data
List<SigningData> signingData = [];
final List<SigningData> signingData = [];
try {
// Populating the addresses to check
@ -879,7 +888,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
DerivePathType type,
int chain,
) async {
List<Address> addressArray = [];
final List<Address> addressArray = [];
int gapCounter = 0;
int highestIndexWithHistory = 0;
@ -891,7 +900,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
"index: $index, \t GapCounter $chain ${type.name}: $gapCounter",
level: LogLevel.Info);
List<String> txCountCallArgs = [];
final List<String> txCountCallArgs = [];
for (int j = 0; j < txCountBatchSize; j++) {
final derivePath = cryptoCurrency.constructDerivePath(
@ -960,7 +969,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
DerivePathType type,
int chain,
) async {
List<Address> addressArray = [];
final List<Address> addressArray = [];
int gapCounter = 0;
int index = 0;
for (;
@ -1023,9 +1032,9 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
Iterable<String> allAddresses,
) async {
try {
List<Map<String, dynamic>> allTxHashes = [];
final List<Map<String, dynamic>> allTxHashes = [];
if (serverCanBatch) {
if (await serverCanBatch) {
final Map<int, List<List<dynamic>>> batches = {};
final Map<int, List<String>> batchIndexToAddressListMap = {};
const batchSizeMax = 100;
@ -1080,7 +1089,10 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
return allTxHashes;
} catch (e, s) {
Logging.instance.log("_fetchHistory: $e\n$s", level: LogLevel.Error);
Logging.instance.log(
"$runtimeType._fetchHistory: $e\n$s",
level: LogLevel.Error,
);
rethrow;
}
}
@ -1363,9 +1375,11 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
level: LogLevel.Info,
);
final canBatch = await serverCanBatch;
for (final type in cryptoCurrency.supportedDerivationPathTypes) {
receiveFutures.add(
serverCanBatch
canBatch
? checkGapsBatched(
txCountBatchSize,
root,
@ -1387,7 +1401,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
);
for (final type in cryptoCurrency.supportedDerivationPathTypes) {
changeFutures.add(
serverCanBatch
canBatch
? checkGapsBatched(
txCountBatchSize,
root,
@ -1510,7 +1524,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
try {
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
if (serverCanBatch) {
if (await serverCanBatch) {
final Map<int, List<List<dynamic>>> batchArgs = {};
const batchSizeMax = 10;
int batchNumber = 0;

View file

@ -0,0 +1,108 @@
import 'dart:math';
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
import 'package:flutter/material.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
part 'sw_date_picker.dart';
Future<DateTime?> showSWDatePicker(BuildContext context) async {
final Size size;
if (Util.isDesktop) {
size = const Size(450, 450);
} else {
final _size = MediaQuery.of(context).size;
size = Size(
_size.width - 32,
_size.height >= 550 ? 450 : _size.height - 32,
);
}
print("=====================================");
print(size);
final now = DateTime.now();
final date = await _showDatePickerDialog(
context: context,
value: [now],
dialogSize: size,
config: CalendarDatePicker2WithActionButtonsConfig(
firstDate: DateTime(2007),
lastDate: now,
currentDate: now,
buttonPadding: const EdgeInsets.only(
right: 16,
),
centerAlignModePicker: true,
selectedDayHighlightColor:
Theme.of(context).extension<StackColors>()!.accentColorDark,
daySplashColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark
.withOpacity(0.6),
),
);
return date?.first;
}
Future<List<DateTime?>?> _showDatePickerDialog({
required BuildContext context,
required CalendarDatePicker2WithActionButtonsConfig config,
required Size dialogSize,
List<DateTime?> value = const [],
bool useRootNavigator = true,
bool barrierDismissible = true,
Color? barrierColor = Colors.black54,
bool useSafeArea = true,
RouteSettings? routeSettings,
String? barrierLabel,
TransitionBuilder? builder,
}) {
final dialog = Dialog(
insetPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
backgroundColor: Theme.of(context).extension<StackColors>()!.popupBG,
surfaceTintColor: Colors.transparent,
shadowColor: Colors.transparent,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius * 2,
),
),
clipBehavior: Clip.antiAlias,
child: SizedBox(
width: dialogSize.width,
height: max(dialogSize.height, 410),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_SWDatePicker(
value: value,
config: config.copyWith(openedFromDialog: true),
),
],
),
),
);
return showDialog<List<DateTime?>>(
context: context,
useRootNavigator: useRootNavigator,
routeSettings: routeSettings,
builder: (BuildContext context) {
return builder == null ? dialog : builder(context, dialog);
},
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
useSafeArea: useSafeArea,
);
}

View file

@ -0,0 +1,184 @@
part of 'date_picker.dart';
class _SWDatePicker extends StatefulWidget {
const _SWDatePicker({
super.key,
required this.value,
required this.config,
this.onValueChanged,
this.onDisplayedMonthChanged,
this.onCancelTapped,
this.onOkTapped,
});
final List<DateTime?> value;
/// Called when the user taps 'OK' button
final ValueChanged<List<DateTime?>>? onValueChanged;
/// Called when the user navigates to a new month/year in the picker.
final ValueChanged<DateTime>? onDisplayedMonthChanged;
/// The calendar configurations including action buttons
final CalendarDatePicker2WithActionButtonsConfig config;
/// The callback when cancel button is tapped
final Function? onCancelTapped;
/// The callback when ok button is tapped
final Function? onOkTapped;
@override
State<_SWDatePicker> createState() => _SWDatePickerState();
}
class _SWDatePickerState extends State<_SWDatePicker> {
List<DateTime?> _values = [];
List<DateTime?> _editCache = [];
@override
void initState() {
_values = widget.value;
_editCache = widget.value;
super.initState();
}
@override
void didUpdateWidget(covariant _SWDatePicker oldWidget) {
var isValueSame = oldWidget.value.length == widget.value.length;
if (isValueSame) {
for (int i = 0; i < oldWidget.value.length; i++) {
final isSame =
(oldWidget.value[i] == null && widget.value[i] == null) ||
DateUtils.isSameDay(oldWidget.value[i], widget.value[i]);
if (!isSame) {
isValueSame = false;
break;
}
}
}
if (!isValueSame) {
_values = widget.value;
_editCache = widget.value;
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
hoverColor: Colors.transparent,
highlightColor: Colors.transparent,
focusColor: Colors.transparent,
colorScheme: Theme.of(context).colorScheme.copyWith(
background: Theme.of(context).extension<StackColors>()!.popupBG,
onBackground:
Theme.of(context).extension<StackColors>()!.accentColorDark,
surface: Theme.of(context).extension<StackColors>()!.popupBG,
surfaceVariant:
Theme.of(context).extension<StackColors>()!.popupBG,
onSurface:
Theme.of(context).extension<StackColors>()!.accentColorDark,
onSurfaceVariant:
Theme.of(context).extension<StackColors>()!.accentColorDark,
surfaceTint: Colors.transparent,
shadow: Colors.transparent,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
MediaQuery.removePadding(
context: context,
child: CalendarDatePicker2(
value: [..._editCache],
config: widget.config,
onValueChanged: (values) => _editCache = values,
onDisplayedMonthChanged: widget.onDisplayedMonthChanged,
),
),
SizedBox(height: widget.config.gapBetweenCalendarAndButtons ?? 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (!Util.isDesktop)
SizedBox(
width: widget.config.buttonPadding?.right ?? 0,
),
ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Expanded(
child: child,
),
child: Padding(
padding: widget.config.buttonPadding ?? EdgeInsets.zero,
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => SizedBox(
width: 140,
child: child,
),
child: SecondaryButton(
label: "Cancel",
buttonHeight: Util.isDesktop ? ButtonHeight.m : null,
onPressed: () {
setState(
() {
_editCache = _values;
widget.onCancelTapped?.call();
if ((widget.config.openedFromDialog ?? false) &&
(widget.config.closeDialogOnCancelTapped ??
true)) {
Navigator.pop(context);
}
},
);
},
),
),
),
),
if ((widget.config.gapBetweenCalendarAndButtons ?? 0) > 0)
SizedBox(width: widget.config.gapBetweenCalendarAndButtons),
ConditionalParent(
condition: !Util.isDesktop,
builder: (child) => Expanded(
child: child,
),
child: Padding(
padding: widget.config.buttonPadding ?? EdgeInsets.zero,
child: ConditionalParent(
condition: Util.isDesktop,
builder: (child) => SizedBox(
width: 140,
child: child,
),
child: PrimaryButton(
buttonHeight: Util.isDesktop ? ButtonHeight.m : null,
label: "Ok",
onPressed: () {
setState(
() {
_values = _editCache;
widget.onValueChanged?.call(_values);
widget.onOkTapped?.call();
if ((widget.config.openedFromDialog ?? false) &&
(widget.config.closeDialogOnOkTapped ?? true)) {
Navigator.pop(context, _values);
}
},
);
},
),
),
),
),
],
),
],
),
);
}
}

View file

@ -22,10 +22,10 @@ import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
class WalletInfoRowBalance extends ConsumerWidget {
const WalletInfoRowBalance({
Key? key,
super.key,
required this.walletId,
this.contractAddress,
}) : super(key: key);
});
final String walletId;
final String? contractAddress;
@ -45,8 +45,11 @@ class WalletInfoRowBalance extends ConsumerWidget {
} else {
contract = MainDB.instance.getEthContractSync(contractAddress!)!;
totalBalance = ref
.watch(pTokenBalance(
(contractAddress: contractAddress!, walletId: walletId)))
.watch(
pTokenBalance(
(walletId: walletId, contractAddress: contractAddress!),
),
)
.total;
}

View file

@ -8,29 +8,36 @@
*
*/
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/themes/theme_providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
class FrostSignNavIcon extends ConsumerWidget {
const FrostSignNavIcon({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return SvgPicture.file(
File(
ref.watch(
themeProvider.select(
// TODO: [prio=high] update themes with icon asset
(value) => value.assets.stackIcon,
),
return Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.bottomNavIconIcon
.withOpacity(0.4),
borderRadius: BorderRadius.circular(
24,
),
),
child: Padding(
padding: const EdgeInsets.all(6.0),
child: SvgPicture.asset(
Assets.svg.pencil,
width: 12,
height: 12,
color: Theme.of(context).extension<StackColors>()!.bottomNavIconIcon,
),
),
width: 24,
height: 24,
);
}
}

View file

@ -238,6 +238,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.6.2"
calendar_date_picker2:
dependency: "direct main"
description:
name: calendar_date_picker2
sha256: "7ff3f372faff6814a2ba69427d116fb9a3d52e28644b9de4b06db6638fdac798"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
characters:
dependency: transitive
description:
@ -757,14 +765,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
flutter_rounded_date_picker:
dependency: "direct main"
description:
name: flutter_rounded_date_picker
sha256: e6aa2dc5d3b44e8bbe85ef901be69eac59ba4136427f11f4c8b2a303e1e774e7
url: "https://pub.dev"
source: hosted
version: "3.0.4"
flutter_secure_storage:
dependency: "direct main"
description:
@ -1740,8 +1740,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: f31f8f857665d85338824ae171aba4c629c3ba6f
resolved-ref: f31f8f857665d85338824ae171aba4c629c3ba6f
ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad"
resolved-ref: "13fa937ea9a9fc34caf047e068df9535f65c27ad"
url: "https://github.com/cypherstack/tezart.git"
source: git
version: "2.0.5"
@ -2109,5 +2109,5 @@ packages:
source: hosted
version: "1.0.0"
sdks:
dart: ">=3.3.3 <4.0.0"
flutter: ">=3.19.5"
dart: ">=3.3.4 <4.0.0"
flutter: ">=3.19.6"

View file

@ -14,8 +14,8 @@ description: Stack Wallet
version: 2.0.0+219
environment:
sdk: ">=3.3.3 <4.0.0"
flutter: ^3.19.5
sdk: ">=3.3.4 <4.0.0"
flutter: ^3.19.6
dependencies:
flutter:
@ -124,7 +124,6 @@ dependencies:
decimal: ^2.1.0
event_bus: ^2.0.0
uuid: ^3.0.5
flutter_rounded_date_picker: ^3.0.1
crypto: ^3.0.2
barcode_scan2: ^4.2.3
wakelock: ^0.6.2
@ -178,6 +177,7 @@ dependencies:
url: https://github.com/cypherstack/espresso-cash-public.git
ref: a83e375678eb22fe544dc125d29bbec0fb833882
path: packages/solana
calendar_date_picker2: ^1.0.2
dev_dependencies:
flutter_test: