mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 03:49:22 +00:00
desktop forgot password flow and functionality
This commit is contained in:
parent
6a17ddffdf
commit
7436709fa7
11 changed files with 813 additions and 115 deletions
122
lib/hive/db.dart
122
lib/hive/db.dart
|
@ -40,8 +40,6 @@ class DB {
|
|||
String boxNameUsedSerialsCache({required Coin coin}) =>
|
||||
"${coin.name}_usedSerialsCache";
|
||||
|
||||
static bool _initialized = false;
|
||||
|
||||
Box<dynamic>? _boxAddressBook;
|
||||
Box<String>? _boxDebugInfo;
|
||||
Box<NodeModel>? _boxNodeModels;
|
||||
|
@ -88,69 +86,65 @@ class DB {
|
|||
|
||||
// open hive boxes
|
||||
Future<void> init() async {
|
||||
if (!_initialized) {
|
||||
if (Hive.isBoxOpen(boxNameDBInfo)) {
|
||||
_boxDBInfo = Hive.box<dynamic>(boxNameDBInfo);
|
||||
} else {
|
||||
_boxDBInfo = await Hive.openBox<dynamic>(boxNameDBInfo);
|
||||
}
|
||||
await Hive.openBox<String>(boxNameWalletsToDeleteOnStart);
|
||||
|
||||
if (Hive.isBoxOpen(boxNamePrefs)) {
|
||||
_boxPrefs = Hive.box<dynamic>(boxNamePrefs);
|
||||
} else {
|
||||
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
|
||||
}
|
||||
|
||||
_boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook);
|
||||
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
|
||||
|
||||
if (Hive.isBoxOpen(boxNameNodeModels)) {
|
||||
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
|
||||
} else {
|
||||
_boxNodeModels = await Hive.openBox<NodeModel>(boxNameNodeModels);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNamePrimaryNodes)) {
|
||||
_boxPrimaryNodes = Hive.box<NodeModel>(boxNamePrimaryNodes);
|
||||
} else {
|
||||
_boxPrimaryNodes = await Hive.openBox<NodeModel>(boxNamePrimaryNodes);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNameAllWalletsData)) {
|
||||
_boxAllWalletsData = Hive.box<dynamic>(boxNameAllWalletsData);
|
||||
} else {
|
||||
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNameDesktopData)) {
|
||||
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
|
||||
} else {
|
||||
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
|
||||
}
|
||||
|
||||
_boxNotifications =
|
||||
await Hive.openBox<NotificationModel>(boxNameNotifications);
|
||||
_boxWatchedTransactions =
|
||||
await Hive.openBox<NotificationModel>(boxNameWatchedTransactions);
|
||||
_boxWatchedTrades =
|
||||
await Hive.openBox<NotificationModel>(boxNameWatchedTrades);
|
||||
_boxTrades = await Hive.openBox<ExchangeTransaction>(boxNameTrades);
|
||||
_boxTradesV2 = await Hive.openBox<Trade>(boxNameTradesV2);
|
||||
_boxTradeNotes = await Hive.openBox<String>(boxNameTradeNotes);
|
||||
_boxTradeLookup =
|
||||
await Hive.openBox<TradeWalletLookup>(boxNameTradeLookup);
|
||||
_walletInfoSource =
|
||||
await Hive.openBox<xmr.WalletInfo>(xmr.WalletInfo.boxName);
|
||||
_boxFavoriteWallets = await Hive.openBox<String>(boxNameFavoriteWallets);
|
||||
|
||||
await Future.wait([
|
||||
Hive.openBox<dynamic>(boxNamePriceCache),
|
||||
_loadWalletBoxes(),
|
||||
_loadSharedCoinCacheBoxes(),
|
||||
]);
|
||||
_initialized = true;
|
||||
if (Hive.isBoxOpen(boxNameDBInfo)) {
|
||||
_boxDBInfo = Hive.box<dynamic>(boxNameDBInfo);
|
||||
} else {
|
||||
_boxDBInfo = await Hive.openBox<dynamic>(boxNameDBInfo);
|
||||
}
|
||||
await Hive.openBox<String>(boxNameWalletsToDeleteOnStart);
|
||||
|
||||
if (Hive.isBoxOpen(boxNamePrefs)) {
|
||||
_boxPrefs = Hive.box<dynamic>(boxNamePrefs);
|
||||
} else {
|
||||
_boxPrefs = await Hive.openBox<dynamic>(boxNamePrefs);
|
||||
}
|
||||
|
||||
_boxAddressBook = await Hive.openBox<dynamic>(boxNameAddressBook);
|
||||
_boxDebugInfo = await Hive.openBox<String>(boxNameDebugInfo);
|
||||
|
||||
if (Hive.isBoxOpen(boxNameNodeModels)) {
|
||||
_boxNodeModels = Hive.box<NodeModel>(boxNameNodeModels);
|
||||
} else {
|
||||
_boxNodeModels = await Hive.openBox<NodeModel>(boxNameNodeModels);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNamePrimaryNodes)) {
|
||||
_boxPrimaryNodes = Hive.box<NodeModel>(boxNamePrimaryNodes);
|
||||
} else {
|
||||
_boxPrimaryNodes = await Hive.openBox<NodeModel>(boxNamePrimaryNodes);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNameAllWalletsData)) {
|
||||
_boxAllWalletsData = Hive.box<dynamic>(boxNameAllWalletsData);
|
||||
} else {
|
||||
_boxAllWalletsData = await Hive.openBox<dynamic>(boxNameAllWalletsData);
|
||||
}
|
||||
|
||||
if (Hive.isBoxOpen(boxNameDesktopData)) {
|
||||
_boxDesktopData = Hive.box<String>(boxNameDesktopData);
|
||||
} else {
|
||||
_boxDesktopData = await Hive.openBox<String>(boxNameDesktopData);
|
||||
}
|
||||
|
||||
_boxNotifications =
|
||||
await Hive.openBox<NotificationModel>(boxNameNotifications);
|
||||
_boxWatchedTransactions =
|
||||
await Hive.openBox<NotificationModel>(boxNameWatchedTransactions);
|
||||
_boxWatchedTrades =
|
||||
await Hive.openBox<NotificationModel>(boxNameWatchedTrades);
|
||||
_boxTrades = await Hive.openBox<ExchangeTransaction>(boxNameTrades);
|
||||
_boxTradesV2 = await Hive.openBox<Trade>(boxNameTradesV2);
|
||||
_boxTradeNotes = await Hive.openBox<String>(boxNameTradeNotes);
|
||||
_boxTradeLookup = await Hive.openBox<TradeWalletLookup>(boxNameTradeLookup);
|
||||
_walletInfoSource =
|
||||
await Hive.openBox<xmr.WalletInfo>(xmr.WalletInfo.boxName);
|
||||
_boxFavoriteWallets = await Hive.openBox<String>(boxNameFavoriteWallets);
|
||||
|
||||
await Future.wait([
|
||||
Hive.openBox<dynamic>(boxNamePriceCache),
|
||||
_loadWalletBoxes(),
|
||||
_loadSharedCoinCacheBoxes(),
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> _loadWalletBoxes() async {
|
||||
|
|
|
@ -2,12 +2,14 @@ import 'package:flutter/gestures.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/stack_privacy_calls.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/prefs.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/background.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class IntroView extends StatefulWidget {
|
||||
|
@ -135,6 +137,20 @@ class _IntroViewState extends State<IntroView> {
|
|||
GetStartedButton(
|
||||
isDesktop: isDesktop,
|
||||
),
|
||||
if (isDesktop)
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
if (isDesktop)
|
||||
SecondaryButton(
|
||||
label: "Restore from Stack backup",
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
CreatePasswordView.routeName,
|
||||
arguments: true,
|
||||
);
|
||||
},
|
||||
),
|
||||
const Spacer(
|
||||
flex: 65,
|
||||
),
|
||||
|
@ -257,7 +273,7 @@ class GetStartedButton extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: 328,
|
||||
width: double.infinity,
|
||||
height: 70,
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
|
@ -270,7 +286,7 @@ class GetStartedButton extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
child: Text(
|
||||
"Get started",
|
||||
"Create new Stack",
|
||||
style: STextStyles.button(context).copyWith(fontSize: 20),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
|
|||
import 'package:stackwallet/route_generator.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
|
|
@ -9,6 +9,9 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
|
|||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_item_card.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_widgets/restoring_wallet_card.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_menu.dart';
|
||||
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/providers/stack_restore/stack_restoring_ui_state_provider.dart';
|
||||
|
@ -20,25 +23,23 @@ 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/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_container.dart';
|
||||
|
||||
import '../../../../../pages_desktop_specific/desktop_home_view.dart';
|
||||
import '../../../../../pages_desktop_specific/desktop_menu.dart';
|
||||
import '../../../../../providers/desktop/current_desktop_menu_item.dart';
|
||||
import '../../../../../widgets/desktop/primary_button.dart';
|
||||
|
||||
class StackRestoreProgressView extends ConsumerStatefulWidget {
|
||||
const StackRestoreProgressView({
|
||||
Key? key,
|
||||
required this.jsonString,
|
||||
this.fromFile = false,
|
||||
this.shouldPushToHome = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final String jsonString;
|
||||
final bool fromFile;
|
||||
final bool shouldPushToHome;
|
||||
|
||||
@override
|
||||
ConsumerState<StackRestoreProgressView> createState() =>
|
||||
|
@ -696,11 +697,21 @@ class _StackRestoreProgressViewState
|
|||
.state)
|
||||
.state = keyID;
|
||||
|
||||
Navigator.of(context, rootNavigator: true)
|
||||
.popUntil(
|
||||
ModalRoute.withName(
|
||||
DesktopHomeView.routeName),
|
||||
);
|
||||
if (widget.shouldPushToHome) {
|
||||
unawaited(
|
||||
Navigator.of(context)
|
||||
.pushNamedAndRemoveUntil(
|
||||
DesktopHomeView.routeName,
|
||||
(route) => false,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.of(context, rootNavigator: true)
|
||||
.popUntil(
|
||||
ModalRoute.withName(
|
||||
DesktopHomeView.routeName),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
: SecondaryButton(
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart';
|
||||
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
|
@ -23,9 +24,11 @@ import 'package:zxcvbn/zxcvbn.dart';
|
|||
class CreatePasswordView extends ConsumerStatefulWidget {
|
||||
const CreatePasswordView({
|
||||
Key? key,
|
||||
this.restoreFromSWB = false,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/createPasswordDesktop";
|
||||
final bool restoreFromSWB;
|
||||
|
||||
@override
|
||||
ConsumerState<CreatePasswordView> createState() => _CreatePasswordViewState();
|
||||
|
@ -84,7 +87,9 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
|||
|
||||
// load default nodes now as node service requires storage handler to exist
|
||||
|
||||
await ref.read(nodeServiceChangeNotifierProvider).updateDefaults();
|
||||
if (!widget.restoreFromSWB) {
|
||||
await ref.read(nodeServiceChangeNotifierProvider).updateDefaults();
|
||||
}
|
||||
} catch (e) {
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
|
@ -95,15 +100,28 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
|
|||
}
|
||||
|
||||
if (mounted) {
|
||||
unawaited(Navigator.of(context)
|
||||
.pushReplacementNamed(DesktopHomeView.routeName));
|
||||
if (widget.restoreFromSWB) {
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
ForgottenPassphraseRestoreFromSWB.routeName,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
unawaited(
|
||||
Navigator.of(context).pushReplacementNamed(
|
||||
DesktopHomeView.routeName,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.success,
|
||||
message: "Your password is set up",
|
||||
context: context,
|
||||
));
|
||||
if (!widget.restoreFromSWB) {
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.success,
|
||||
message: "Your password is set up",
|
||||
context: context,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:stackwallet/hive/db.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/intro_view.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.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_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
|
||||
class DeletePasswordWarningView extends ConsumerStatefulWidget {
|
||||
const DeletePasswordWarningView({
|
||||
Key? key,
|
||||
required this.shouldCreateNew,
|
||||
}) : super(key: key);
|
||||
|
||||
static const String routeName = "/deletePasswordWarning";
|
||||
|
||||
final bool shouldCreateNew;
|
||||
|
||||
@override
|
||||
ConsumerState<DeletePasswordWarningView> createState() =>
|
||||
_ForgotPasswordDesktopViewState();
|
||||
}
|
||||
|
||||
class _ForgotPasswordDesktopViewState
|
||||
extends ConsumerState<DeletePasswordWarningView> {
|
||||
bool _deleteInProgress = false;
|
||||
|
||||
Future<bool> _deleteStack() async {
|
||||
final appRoot = await StackFileSystem.applicationRootDirectory();
|
||||
|
||||
try {
|
||||
await Hive.close();
|
||||
await appRoot.delete(recursive: true);
|
||||
await DB.instance.init();
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"$e\n$s",
|
||||
level: LogLevel.Fatal,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopScaffold(
|
||||
appBar: DesktopAppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (mounted && !_deleteInProgress) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
isCompactHeight: false,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 480,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.svg.stackIcon(context),
|
||||
width: 100,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 42,
|
||||
),
|
||||
Text(
|
||||
"Warning!",
|
||||
style: STextStyles.desktopH1(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
SizedBox(
|
||||
width: 480,
|
||||
child: RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "To ",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
TextSpan(
|
||||
text: "create a new Stack",
|
||||
style: STextStyles.desktopTextSmallBold(context),
|
||||
),
|
||||
TextSpan(
|
||||
text: ", we need to ",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
TextSpan(
|
||||
text: "delete your old wallets",
|
||||
style: STextStyles.desktopTextSmallBold(context),
|
||||
),
|
||||
TextSpan(
|
||||
text:
|
||||
". All wallets will be lost. If you have not written down your recovery phrase for EACH wallet, you may be in danger of losing funds. Continue?",
|
||||
style: STextStyles.desktopTextSmall(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 48,
|
||||
),
|
||||
PrimaryButton(
|
||||
label: "Delete and continue",
|
||||
enabled: !_deleteInProgress,
|
||||
onPressed: () async {
|
||||
final shouldDelete = !_deleteInProgress;
|
||||
setState(() {
|
||||
_deleteInProgress = true;
|
||||
});
|
||||
|
||||
if (shouldDelete) {
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Deleting wallet...",
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
|
||||
final success = await _deleteStack();
|
||||
|
||||
if (success) {
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.success,
|
||||
message: "Wallet deleted",
|
||||
context: context,
|
||||
);
|
||||
if (mounted) {
|
||||
await Navigator.of(context).pushNamedAndRemoveUntil(
|
||||
IntroView.routeName,
|
||||
(_) => false,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Something broke badly. Contact developer",
|
||||
context: context,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_deleteInProgress = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
SecondaryButton(
|
||||
label: "Take me back!",
|
||||
enabled: !_deleteInProgress,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: kDesktopAppBarHeight,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
@ -74,18 +75,26 @@ class _ForgotPasswordDesktopViewState extends State<ForgotPasswordDesktopView> {
|
|||
height: 48,
|
||||
),
|
||||
PrimaryButton(
|
||||
label: "Create new wallet",
|
||||
label: "Create new Stack",
|
||||
onPressed: () {
|
||||
// // todo delete everything and start fresh?
|
||||
const shouldCreateNew = true;
|
||||
Navigator.of(context).pushNamed(
|
||||
DeletePasswordWarningView.routeName,
|
||||
arguments: shouldCreateNew,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
SecondaryButton(
|
||||
label: "Restore from backup",
|
||||
label: "Restore from Stack backup",
|
||||
onPressed: () {
|
||||
// todo SWB restore
|
||||
const shouldCreateNew = false;
|
||||
Navigator.of(context).pushNamed(
|
||||
DeletePasswordWarningView.routeName,
|
||||
arguments: shouldCreateNew,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -0,0 +1,406 @@
|
|||
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';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:stackwallet/hive/db.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
|
||||
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
|
||||
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
|
||||
import 'package:stackwallet/providers/global/secure_store_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/flutter_secure_storage_interface.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class ForgottenPassphraseRestoreFromSWB extends ConsumerStatefulWidget {
|
||||
const ForgottenPassphraseRestoreFromSWB({Key? key}) : super(key: key);
|
||||
|
||||
static const String routeName = "/forgottenPassphraseRestoreFromSWB";
|
||||
|
||||
@override
|
||||
ConsumerState<ForgottenPassphraseRestoreFromSWB> createState() =>
|
||||
_ForgottenPassphraseRestoreFromSWBState();
|
||||
}
|
||||
|
||||
class _ForgottenPassphraseRestoreFromSWBState
|
||||
extends ConsumerState<ForgottenPassphraseRestoreFromSWB> {
|
||||
late final TextEditingController fileLocationController;
|
||||
late final TextEditingController passwordController;
|
||||
|
||||
late final FocusNode passwordFocusNode;
|
||||
|
||||
late final SWBFileSystem stackFileSystem;
|
||||
|
||||
bool hidePassword = true;
|
||||
|
||||
bool _enableButton = false;
|
||||
|
||||
Future<void> restore() async {
|
||||
final String fileToRestore = fileLocationController.text;
|
||||
final String passphrase = passwordController.text;
|
||||
|
||||
if (!(await File(fileToRestore).exists())) {
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Backup file does not exist",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldPop = false;
|
||||
unawaited(
|
||||
showDialog<dynamic>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (_) => WillPopScope(
|
||||
onWillPop: () async {
|
||||
return shouldPop;
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Decrypting Stack backup file",
|
||||
style: STextStyles.pageTitleH2(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textWhite,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 64,
|
||||
),
|
||||
const Center(
|
||||
child: LoadingIndicator(
|
||||
width: 100,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final String? jsonString = await compute(
|
||||
SWB.decryptStackWalletWithPassphrase,
|
||||
Tuple2(fileToRestore, passphrase),
|
||||
debugLabel: "stack wallet decryption compute",
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
// pop LoadingIndicator
|
||||
shouldPop = true;
|
||||
Navigator.of(context).pop();
|
||||
|
||||
passwordController.text = "";
|
||||
|
||||
if (jsonString == null) {
|
||||
await showFloatingFlushBar(
|
||||
type: FlushBarType.warning,
|
||||
message: "Failed to decrypt backup file",
|
||||
context: context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(walletsChangeNotifierProvider);
|
||||
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 580,
|
||||
maxHeight: double.infinity,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Restoring Stack wallet",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 44,
|
||||
),
|
||||
StackRestoreProgressView(
|
||||
jsonString: jsonString,
|
||||
shouldPushToHome: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
// await Navigator.of(context).push(
|
||||
// RouteGenerator.getRoute(
|
||||
// builder: (_) => StackRestoreProgressView(
|
||||
// jsonString: jsonString,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
stackFileSystem = SWBFileSystem();
|
||||
fileLocationController = TextEditingController();
|
||||
passwordController = TextEditingController();
|
||||
|
||||
passwordFocusNode = FocusNode();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
fileLocationController.dispose();
|
||||
passwordController.dispose();
|
||||
|
||||
passwordFocusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopScaffold(
|
||||
appBar: DesktopAppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
await (ref.read(secureStoreProvider).store as DesktopSecureStore)
|
||||
.close();
|
||||
ref.refresh(secureStoreProvider);
|
||||
ref.refresh(storageCryptoHandlerProvider);
|
||||
await Hive.deleteBoxFromDisk(DB.boxNameDesktopData);
|
||||
await DB.instance.init();
|
||||
if (mounted) {
|
||||
Navigator.of(context)
|
||||
.popUntil(ModalRoute.withName(CreatePasswordView.routeName));
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
isCompactHeight: false,
|
||||
),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 480,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Restore from backup",
|
||||
style: STextStyles.desktopH1(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 32,
|
||||
),
|
||||
Text(
|
||||
"Use your Stack wallet backup file to restore your wallets, address book, and wallet preferences.",
|
||||
textAlign: TextAlign.center,
|
||||
style: STextStyles.desktopTextSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 40,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
try {
|
||||
await stackFileSystem.prepareStorage();
|
||||
if (mounted) {
|
||||
await stackFileSystem.openFile(context);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
fileLocationController.text =
|
||||
stackFileSystem.filePath ?? "";
|
||||
});
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: IgnorePointer(
|
||||
child: TextField(
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
controller: fileLocationController,
|
||||
style: STextStyles.field(context),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Choose file...",
|
||||
hintStyle: STextStyles.desktopTextFieldLabel(context),
|
||||
suffixIcon: SizedBox(
|
||||
height: 70,
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 24,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.folder,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
key: const Key("restoreFromFileLocationTextFieldKey"),
|
||||
readOnly: true,
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: true,
|
||||
cut: false,
|
||||
paste: false,
|
||||
selectAll: false,
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
_enableButton =
|
||||
passwordController.text.isNotEmpty &&
|
||||
fileLocationController.text.isNotEmpty;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key("restoreFromFilePasswordFieldKey"),
|
||||
focusNode: passwordFocusNode,
|
||||
controller: passwordController,
|
||||
style: STextStyles.desktopTextMedium(context).copyWith(
|
||||
height: 2,
|
||||
),
|
||||
obscureText: hidePassword,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
decoration: standardInputDecoration(
|
||||
"Enter passphrase",
|
||||
passwordFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
suffixIcon: UnconstrainedBox(
|
||||
child: SizedBox(
|
||||
height: 70,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 24,
|
||||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"restoreFromFilePasswordFieldShowPasswordButtonKey"),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword = !hidePassword;
|
||||
});
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: SvgPicture.asset(
|
||||
hidePassword
|
||||
? Assets.svg.eye
|
||||
: Assets.svg.eyeSlash,
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
_enableButton = passwordController.text.isNotEmpty &&
|
||||
fileLocationController.text.isNotEmpty;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
PrimaryButton(
|
||||
label: "Restore",
|
||||
enabled: _enableButton,
|
||||
onPressed: () {
|
||||
restore();
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: kDesktopAppBarHeight,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -98,7 +98,9 @@ import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub
|
|||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/notifications/desktop_notifications_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/create_password_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/delete_password_warning_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/forgot_password_desktop_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/password/forgotten_passphrase_restore_from_swb.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/settings/desktop_settings_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/appearance_settings.dart';
|
||||
|
@ -999,6 +1001,17 @@ class RouteGenerator {
|
|||
|
||||
// == Desktop specific routes ============================================
|
||||
case CreatePasswordView.routeName:
|
||||
if (args is bool) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => CreatePasswordView(
|
||||
restoreFromSWB: args,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => const CreatePasswordView(),
|
||||
|
@ -1010,6 +1023,26 @@ class RouteGenerator {
|
|||
builder: (_) => const ForgotPasswordDesktopView(),
|
||||
settings: RouteSettings(name: settings.name));
|
||||
|
||||
case ForgottenPassphraseRestoreFromSWB.routeName:
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => const ForgottenPassphraseRestoreFromSWB(),
|
||||
settings: RouteSettings(name: settings.name));
|
||||
|
||||
case DeletePasswordWarningView.routeName:
|
||||
if (args is bool) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => DeletePasswordWarningView(
|
||||
shouldCreateNew: args,
|
||||
),
|
||||
settings: RouteSettings(
|
||||
name: settings.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case DesktopHomeView.routeName:
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
|
|
|
@ -997,6 +997,32 @@ class STextStyles {
|
|||
}
|
||||
}
|
||||
|
||||
static TextStyle desktopTextSmallBold(BuildContext context) {
|
||||
switch (_theme(context).themeType) {
|
||||
case ThemeType.light:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
height: 27 / 18,
|
||||
);
|
||||
case ThemeType.oceanBreeze:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).textDark,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
height: 27 / 18,
|
||||
);
|
||||
case ThemeType.dark:
|
||||
return GoogleFonts.inter(
|
||||
color: _theme(context).buttonTextPrimaryDisabled,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
height: 27 / 18,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static TextStyle desktopTextExtraSmall(BuildContext context) {
|
||||
switch (_theme(context).themeType) {
|
||||
case ThemeType.light:
|
||||
|
|
45
pubspec.lock
45
pubspec.lock
|
@ -42,7 +42,7 @@ packages:
|
|||
name: archive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.11"
|
||||
version: "3.3.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -63,7 +63,7 @@ packages:
|
|||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.8.2"
|
||||
version: "2.9.0"
|
||||
barcode_scan2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -190,14 +190,7 @@ packages:
|
|||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.2.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -218,7 +211,7 @@ packages:
|
|||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -288,7 +281,7 @@ packages:
|
|||
name: coverage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.5.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -442,7 +435,7 @@ packages:
|
|||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -871,21 +864,21 @@ packages:
|
|||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.11"
|
||||
version: "0.12.12"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
version: "0.1.5"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.8.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -997,7 +990,7 @@ packages:
|
|||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.1"
|
||||
version: "1.8.2"
|
||||
path_drawing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1373,7 +1366,7 @@ packages:
|
|||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.2"
|
||||
version: "1.9.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1417,7 +1410,7 @@ packages:
|
|||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "1.1.1"
|
||||
string_validator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1431,35 +1424,35 @@ packages:
|
|||
name: sync_http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.3.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.2.1"
|
||||
test:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.21.1"
|
||||
version: "1.21.4"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.9"
|
||||
version: "0.4.12"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.13"
|
||||
version: "0.4.16"
|
||||
time:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1508,7 +1501,7 @@ packages:
|
|||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.3.1"
|
||||
universal_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1592,7 +1585,7 @@ packages:
|
|||
name: vm_service
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "8.2.2"
|
||||
version: "9.0.0"
|
||||
wakelock:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
Loading…
Reference in a new issue