Merge pull request from cypherstack/desktop

Desktop
This commit is contained in:
julian-CStack 2022-11-14 17:00:50 -06:00 committed by GitHub
commit a5af60535e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 7290 additions and 3430 deletions
assets/svg
crypto_plugins
lib
hive
main.dart
models
notifications
pages
pages_desktop_specific
providers
route_generator.dart
services
utilities
widgets
pubspec.lockpubspec.yaml
test

View file

@ -0,0 +1,12 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5370_82626)">
<path d="M9.99935 18.3337C14.6017 18.3337 18.3327 14.6027 18.3327 10.0003C18.3327 5.39795 14.6017 1.66699 9.99935 1.66699C5.39698 1.66699 1.66602 5.39795 1.66602 10.0003C1.66602 14.6027 5.39698 18.3337 9.99935 18.3337Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10 6.66699V13.3337" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.66602 10H13.3327" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5370_82626">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 780 B

@ -1 +1 @@
Subproject commit 2da77438527732dfaa5398aa391eab5253dabe19
Subproject commit de29931dacc9aefaf42a9ca139a8754a42adc40d

View file

@ -33,6 +33,7 @@ class DB {
static const String boxNamePriceCache = "priceAPIPrice24hCache";
static const String boxNameDBInfo = "dbInfo";
static const String boxNameTheme = "theme";
static const String boxNameDesktopData = "desktopData";
String boxNameTxCache({required Coin coin}) => "${coin.name}_txCache";
String boxNameSetCache({required Coin coin}) =>
@ -42,22 +43,23 @@ class DB {
static bool _initialized = false;
late final Box<dynamic> _boxAddressBook;
late final Box<String> _boxDebugInfo;
late final Box<NodeModel> _boxNodeModels;
late final Box<NodeModel> _boxPrimaryNodes;
late final Box<dynamic> _boxAllWalletsData;
late final Box<NotificationModel> _boxNotifications;
late final Box<NotificationModel> _boxWatchedTransactions;
late final Box<NotificationModel> _boxWatchedTrades;
late final Box<ExchangeTransaction> _boxTrades;
late final Box<Trade> _boxTradesV2;
late final Box<String> _boxTradeNotes;
late final Box<String> _boxFavoriteWallets;
late final Box<xmr.WalletInfo> _walletInfoSource;
late final Box<dynamic> _boxPrefs;
late final Box<TradeWalletLookup> _boxTradeLookup;
late final Box<dynamic> _boxDBInfo;
Box<dynamic>? _boxAddressBook;
Box<String>? _boxDebugInfo;
Box<NodeModel>? _boxNodeModels;
Box<NodeModel>? _boxPrimaryNodes;
Box<dynamic>? _boxAllWalletsData;
Box<NotificationModel>? _boxNotifications;
Box<NotificationModel>? _boxWatchedTransactions;
Box<NotificationModel>? _boxWatchedTrades;
Box<ExchangeTransaction>? _boxTrades;
Box<Trade>? _boxTradesV2;
Box<String>? _boxTradeNotes;
Box<String>? _boxFavoriteWallets;
Box<xmr.WalletInfo>? _walletInfoSource;
Box<dynamic>? _boxPrefs;
Box<TradeWalletLookup>? _boxTradeLookup;
Box<dynamic>? _boxDBInfo;
Box<String>? _boxDesktopData;
final Map<String, Box<dynamic>> _walletBoxes = {};
@ -66,7 +68,7 @@ class DB {
final Map<Coin, Box<dynamic>> _usedSerialsCacheBoxes = {};
// exposed for monero
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource;
Box<xmr.WalletInfo> get moneroWalletInfoBox => _walletInfoSource!;
// mutex for stack backup
final mutex = Mutex();
@ -122,6 +124,12 @@ class DB {
_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 =
@ -145,11 +153,11 @@ class DB {
_initialized = true;
try {
if (_boxPrefs.get("familiarity") == null) {
await _boxPrefs.put("familiarity", 0);
if (_boxPrefs!.get("familiarity") == null) {
await _boxPrefs!.put("familiarity", 0);
}
int count = _boxPrefs.get("familiarity") as int;
await _boxPrefs.put("familiarity", count + 1);
int count = _boxPrefs!.get("familiarity") as int;
await _boxPrefs!.put("familiarity", count + 1);
Constants.exchangeForExperiencedUsers(count + 1);
} catch (e, s) {
print("$e $s");
@ -158,7 +166,7 @@ class DB {
}
Future<void> _loadWalletBoxes() async {
final names = _boxAllWalletsData.get("names") as Map? ?? {};
final names = _boxAllWalletsData!.get("names") as Map? ?? {};
names.removeWhere((name, dyn) {
final jsonObject = Map<String, dynamic>.from(dyn as Map);
try {

View file

@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_libmonero/monero/monero.dart';
import 'package:flutter_libmonero/wownero/wownero.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:isar/isar.dart';
@ -47,12 +48,12 @@ import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/notifications_api.dart';
import 'package:stackwallet/services/notifications_service.dart';
import 'package:stackwallet/services/trade_service.dart';
import 'package:stackwallet/services/wallets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/db_version_migration.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/utilities/theme/color_theme.dart';
import 'package:stackwallet/utilities/theme/dark_colors.dart';
import 'package:stackwallet/utilities/theme/light_colors.dart';
@ -79,19 +80,11 @@ void main() async {
setWindowMaxSize(Size.infinite);
}
Directory appDirectory = (await getApplicationDocumentsDirectory());
if (Platform.isIOS) {
appDirectory = (await getLibraryDirectory());
}
if (Platform.isLinux || Logging.isArmLinux) {
appDirectory = Directory("${appDirectory.path}/.stackwallet");
await appDirectory.create();
}
// FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
if (!(Logging.isArmLinux || Logging.isTestEnv)) {
final isar = await Isar.open(
[LogSchema],
directory: appDirectory.path,
directory: (await StackFileSystem.applicationIsarDirectory()).path,
inspector: false,
);
await Logging.instance.init(isar);
@ -140,18 +133,29 @@ void main() async {
Hive.registerAdapter(WalletTypeAdapter());
Hive.registerAdapter(UnspentCoinsInfoAdapter());
await Hive.initFlutter(appDirectory.path);
await Hive.initFlutter(
(await StackFileSystem.applicationHiveDirectory()).path);
await Hive.openBox<dynamic>(DB.boxNameDBInfo);
int dbVersion = DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
0;
if (dbVersion < Constants.currentHiveDbVersion) {
try {
await DbVersionMigrator().migrate(dbVersion);
} catch (e, s) {
Logging.instance.log("Cannot migrate database\n$e $s",
level: LogLevel.Error, printFullLength: true);
// todo: db migrate stuff for desktop needs to be handled eventually
if (!Util.isDesktop) {
int dbVersion = DB.instance.get<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
0;
if (dbVersion < Constants.currentHiveDbVersion) {
try {
await DbVersionMigrator().migrate(
dbVersion,
secureStore: const SecureStorageWrapper(
store: FlutterSecureStorage(),
isDesktop: false,
),
);
} catch (e, s) {
Logging.instance.log("Cannot migrate database\n$e $s",
level: LogLevel.Error, printFullLength: true);
}
}
}
@ -199,8 +203,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
static const platform = MethodChannel("STACK_WALLET_RESTORE");
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
late final Wallets _wallets;
late final Prefs _prefs;
// late final Wallets _wallets;
// late final Prefs _prefs;
late final NotificationsService _notificationsService;
late final NodeService _nodeService;
late final TradesService _tradesService;
@ -208,8 +212,24 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
late final Completer<void> loadingCompleter;
bool didLoad = false;
bool didLoadShared = false;
bool _desktopHasPassword = false;
Future<void> loadShared() async {
if (didLoadShared) {
return;
}
didLoadShared = true;
await DB.instance.init();
await ref.read(prefsChangeNotifierProvider).init();
if (Util.isDesktop) {
_desktopHasPassword =
await ref.read(storageCryptoHandlerProvider).hasPassword();
}
}
Future<void> load() async {
try {
if (didLoad) {
@ -217,19 +237,15 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
}
didLoad = true;
await DB.instance.init();
await _prefs.init();
if (Util.isDesktop) {
_desktopHasPassword =
await ref.read(storageCryptoHandlerProvider).hasPassword();
if (!Util.isDesktop) {
await loadShared();
}
_notificationsService = ref.read(notificationsProvider);
_nodeService = ref.read(nodeServiceChangeNotifierProvider);
_tradesService = ref.read(tradesServiceProvider);
NotificationApi.prefs = _prefs;
NotificationApi.prefs = ref.read(prefsChangeNotifierProvider);
NotificationApi.notificationsService = _notificationsService;
unawaited(ref.read(baseCurrenciesProvider).update());
@ -238,23 +254,25 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
await _notificationsService.init(
nodeService: _nodeService,
tradesService: _tradesService,
prefs: _prefs,
prefs: ref.read(prefsChangeNotifierProvider),
);
ref.read(priceAnd24hChangeNotifierProvider).start(true);
await _wallets.load(_prefs);
await ref
.read(walletsChangeNotifierProvider)
.load(ref.read(prefsChangeNotifierProvider));
loadingCompleter.complete();
// TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
// unawaited(_nodeService.updateCommunityNodes());
// run without awaiting
if (Constants.enableExchange &&
_prefs.externalCalls &&
await _prefs.isExternalCallsSet()) {
ref.read(prefsChangeNotifierProvider).externalCalls &&
await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) {
unawaited(ExchangeDataLoadingService().loadAll(ref));
}
if (_prefs.isAutoBackupEnabled) {
switch (_prefs.backupFrequencyType) {
if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled) {
switch (ref.read(prefsChangeNotifierProvider).backupFrequencyType) {
case BackupFrequencyType.everyTenMinutes:
ref.read(autoSWBServiceProvider).startPeriodicBackupTimer(
duration: const Duration(minutes: 10));
@ -294,9 +312,6 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
.read(localeServiceChangeNotifierProvider.notifier)
.loadLocale(notify: false);
_prefs = ref.read(prefsChangeNotifierProvider);
_wallets = ref.read(walletsChangeNotifierProvider);
WidgetsBinding.instance.addPostFrameCallback((_) async {
ref.read(colorThemeProvider.state).state =
StackColors.fromStackColorTheme(
@ -401,7 +416,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
}
Future<void> goToRestoreSWB(String encrypted) async {
if (!_prefs.hasPin) {
if (!ref.read(prefsChangeNotifierProvider).hasPin) {
await Navigator.of(navigatorKey.currentContext!)
.pushNamed(CreatePinView.routeName, arguments: true)
.then((value) {
@ -547,51 +562,70 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
_buildOutlineInputBorder(colorScheme.textFieldDefaultBG),
),
),
home: FutureBuilder(
future: load(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// FlutterNativeSplash.remove();
if (Util.isDesktop &&
(_wallets.hasWallets || _desktopHasPassword)) {
String? startupWalletId;
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
startupWalletId =
ref.read(prefsChangeNotifierProvider).startupWalletId;
}
home: Util.isDesktop
? FutureBuilder(
future: loadShared(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (_desktopHasPassword) {
String? startupWalletId;
if (ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup) {
startupWalletId =
ref.read(prefsChangeNotifierProvider).startupWalletId;
}
return DesktopLoginView(startupWalletId: startupWalletId);
} else if (!Util.isDesktop &&
(_wallets.hasWallets || _prefs.hasPin)) {
// return HomeView();
return DesktopLoginView(
startupWalletId: startupWalletId,
load: load,
);
} else {
return const IntroView();
}
} else {
return const LoadingView();
}
},
)
: FutureBuilder(
future: load(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// FlutterNativeSplash.remove();
if (ref.read(walletsChangeNotifierProvider).hasWallets ||
ref.read(prefsChangeNotifierProvider).hasPin) {
// return HomeView();
String? startupWalletId;
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
startupWalletId =
ref.read(prefsChangeNotifierProvider).startupWalletId;
}
String? startupWalletId;
if (ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup) {
startupWalletId =
ref.read(prefsChangeNotifierProvider).startupWalletId;
}
return LockscreenView(
isInitialAppLogin: true,
routeOnSuccess: HomeView.routeName,
routeOnSuccessArguments: startupWalletId,
biometricsAuthenticationTitle: "Unlock Stack",
biometricsLocalizedReason:
"Unlock your stack wallet using biometrics",
biometricsCancelButtonString: "Cancel",
);
} else {
return const IntroView();
}
} else {
// CURRENTLY DISABLED as cannot be animated
// technically not needed as FlutterNativeSplash will overlay
// anything returned here until the future completes but
// FutureBuilder requires you to return something
return const LoadingView();
}
},
),
return LockscreenView(
isInitialAppLogin: true,
routeOnSuccess: HomeView.routeName,
routeOnSuccessArguments: startupWalletId,
biometricsAuthenticationTitle: "Unlock Stack",
biometricsLocalizedReason:
"Unlock your stack wallet using biometrics",
biometricsCancelButtonString: "Cancel",
);
} else {
return const IntroView();
}
} else {
// CURRENTLY DISABLED as cannot be animated
// technically not needed as FlutterNativeSplash will overlay
// anything returned here until the future completes but
// FutureBuilder requires you to return something
return const LoadingView();
}
},
),
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:isar/isar.dart';
part 'encrypted_string_value.g.dart';
@Collection()
class EncryptedStringValue {
Id id = Isar.autoIncrement;
@Index(unique: true, replace: true)
late String key;
late String value;
@override
String toString() {
return "EncryptedStringValue {\n key=$key\n value=$value\n}";
}
}

View file

@ -0,0 +1,748 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'encrypted_string_value.dart';
// **************************************************************************
// IsarCollectionGenerator
// **************************************************************************
// coverage:ignore-file
// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, join_return_with_assignment, avoid_js_rounded_ints, prefer_final_locals
extension GetEncryptedStringValueCollection on Isar {
IsarCollection<EncryptedStringValue> get encryptedStringValues =>
this.collection();
}
const EncryptedStringValueSchema = CollectionSchema(
name: r'EncryptedStringValue',
id: 4826543019451092626,
properties: {
r'key': PropertySchema(
id: 0,
name: r'key',
type: IsarType.string,
),
r'value': PropertySchema(
id: 1,
name: r'value',
type: IsarType.string,
)
},
estimateSize: _encryptedStringValueEstimateSize,
serializeNative: _encryptedStringValueSerializeNative,
deserializeNative: _encryptedStringValueDeserializeNative,
deserializePropNative: _encryptedStringValueDeserializePropNative,
serializeWeb: _encryptedStringValueSerializeWeb,
deserializeWeb: _encryptedStringValueDeserializeWeb,
deserializePropWeb: _encryptedStringValueDeserializePropWeb,
idName: r'id',
indexes: {
r'key': IndexSchema(
id: -4906094122524121629,
name: r'key',
unique: true,
replace: true,
properties: [
IndexPropertySchema(
name: r'key',
type: IndexType.hash,
caseSensitive: true,
)
],
)
},
links: {},
embeddedSchemas: {},
getId: _encryptedStringValueGetId,
getLinks: _encryptedStringValueGetLinks,
attach: _encryptedStringValueAttach,
version: 5,
);
int _encryptedStringValueEstimateSize(
EncryptedStringValue object,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
var bytesCount = offsets.last;
bytesCount += 3 + object.key.length * 3;
bytesCount += 3 + object.value.length * 3;
return bytesCount;
}
int _encryptedStringValueSerializeNative(
EncryptedStringValue object,
IsarBinaryWriter writer,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
writer.writeString(offsets[0], object.key);
writer.writeString(offsets[1], object.value);
return writer.usedBytes;
}
EncryptedStringValue _encryptedStringValueDeserializeNative(
int id,
IsarBinaryReader reader,
List<int> offsets,
Map<Type, List<int>> allOffsets,
) {
final object = EncryptedStringValue();
object.id = id;
object.key = reader.readString(offsets[0]);
object.value = reader.readString(offsets[1]);
return object;
}
P _encryptedStringValueDeserializePropNative<P>(
Id id,
IsarBinaryReader reader,
int propertyId,
int offset,
Map<Type, List<int>> allOffsets,
) {
switch (propertyId) {
case 0:
return (reader.readString(offset)) as P;
case 1:
return (reader.readString(offset)) as P;
default:
throw IsarError('Unknown property with id $propertyId');
}
}
Object _encryptedStringValueSerializeWeb(
IsarCollection<EncryptedStringValue> collection,
EncryptedStringValue object) {
/*final jsObj = IsarNative.newJsObject();*/ throw UnimplementedError();
}
EncryptedStringValue _encryptedStringValueDeserializeWeb(
IsarCollection<EncryptedStringValue> collection, Object jsObj) {
/*final object = EncryptedStringValue();object.id = IsarNative.jsObjectGet(jsObj, r'id') ?? (double.negativeInfinity as int);object.key = IsarNative.jsObjectGet(jsObj, r'key') ?? '';object.value = IsarNative.jsObjectGet(jsObj, r'value') ?? '';*/
//return object;
throw UnimplementedError();
}
P _encryptedStringValueDeserializePropWeb<P>(
Object jsObj, String propertyName) {
switch (propertyName) {
default:
throw IsarError('Illegal propertyName');
}
}
int? _encryptedStringValueGetId(EncryptedStringValue object) {
if (object.id == Isar.autoIncrement) {
return null;
} else {
return object.id;
}
}
List<IsarLinkBase<dynamic>> _encryptedStringValueGetLinks(
EncryptedStringValue object) {
return [];
}
void _encryptedStringValueAttach(
IsarCollection<dynamic> col, Id id, EncryptedStringValue object) {
object.id = id;
}
extension EncryptedStringValueByIndex on IsarCollection<EncryptedStringValue> {
Future<EncryptedStringValue?> getByKey(String key) {
return getByIndex(r'key', [key]);
}
EncryptedStringValue? getByKeySync(String key) {
return getByIndexSync(r'key', [key]);
}
Future<bool> deleteByKey(String key) {
return deleteByIndex(r'key', [key]);
}
bool deleteByKeySync(String key) {
return deleteByIndexSync(r'key', [key]);
}
Future<List<EncryptedStringValue?>> getAllByKey(List<String> keyValues) {
final values = keyValues.map((e) => [e]).toList();
return getAllByIndex(r'key', values);
}
List<EncryptedStringValue?> getAllByKeySync(List<String> keyValues) {
final values = keyValues.map((e) => [e]).toList();
return getAllByIndexSync(r'key', values);
}
Future<int> deleteAllByKey(List<String> keyValues) {
final values = keyValues.map((e) => [e]).toList();
return deleteAllByIndex(r'key', values);
}
int deleteAllByKeySync(List<String> keyValues) {
final values = keyValues.map((e) => [e]).toList();
return deleteAllByIndexSync(r'key', values);
}
Future<int> putByKey(EncryptedStringValue object) {
return putByIndex(r'key', object);
}
int putByKeySync(EncryptedStringValue object, {bool saveLinks = true}) {
return putByIndexSync(r'key', object, saveLinks: saveLinks);
}
Future<List<int>> putAllByKey(List<EncryptedStringValue> objects) {
return putAllByIndex(r'key', objects);
}
List<int> putAllByKeySync(List<EncryptedStringValue> objects,
{bool saveLinks = true}) {
return putAllByIndexSync(r'key', objects, saveLinks: saveLinks);
}
}
extension EncryptedStringValueQueryWhereSort
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QWhere> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhere>
anyId() {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(const IdWhereClause.any());
});
}
}
extension EncryptedStringValueQueryWhere
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QWhereClause> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idEqualTo(int id) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: id,
upper: id,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idNotEqualTo(int id) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
)
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
);
} else {
return query
.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: false),
)
.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: false),
);
}
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idGreaterThan(int id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.greaterThan(lower: id, includeLower: include),
);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idLessThan(int id, {bool include = false}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(
IdWhereClause.lessThan(upper: id, includeUpper: include),
);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
idBetween(
int lowerId,
int upperId, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IdWhereClause.between(
lower: lowerId,
includeLower: includeLower,
upper: upperId,
includeUpper: includeUpper,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
keyEqualTo(String key) {
return QueryBuilder.apply(this, (query) {
return query.addWhereClause(IndexWhereClause.equalTo(
indexName: r'key',
value: [key],
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterWhereClause>
keyNotEqualTo(String key) {
return QueryBuilder.apply(this, (query) {
if (query.whereSort == Sort.asc) {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'key',
lower: [],
upper: [key],
includeUpper: false,
))
.addWhereClause(IndexWhereClause.between(
indexName: r'key',
lower: [key],
includeLower: false,
upper: [],
));
} else {
return query
.addWhereClause(IndexWhereClause.between(
indexName: r'key',
lower: [key],
includeLower: false,
upper: [],
))
.addWhereClause(IndexWhereClause.between(
indexName: r'key',
lower: [],
upper: [key],
includeUpper: false,
));
}
});
}
}
extension EncryptedStringValueQueryFilter on QueryBuilder<EncryptedStringValue,
EncryptedStringValue, QFilterCondition> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idEqualTo(int value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'id',
value: value,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idGreaterThan(
int value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idLessThan(
int value, {
bool include = false,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'id',
value: value,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> idBetween(
int lower,
int upper, {
bool includeLower = true,
bool includeUpper = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'id',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'key',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'key',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'key',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'key',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'key',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> keyEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'key',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition>
keyContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'key',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition>
keyMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'key',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'value',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueGreaterThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'value',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueLessThan(
String value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'value',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueBetween(
String lower,
String upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'value',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'value',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition> valueEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'value',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition>
valueContains(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'value',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue,
QAfterFilterCondition>
valueMatches(String pattern, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'value',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
}
extension EncryptedStringValueQueryObject on QueryBuilder<EncryptedStringValue,
EncryptedStringValue, QFilterCondition> {}
extension EncryptedStringValueQueryLinks on QueryBuilder<EncryptedStringValue,
EncryptedStringValue, QFilterCondition> {}
extension EncryptedStringValueQuerySortBy
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QSortBy> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
sortByKey() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'key', Sort.asc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
sortByKeyDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'key', Sort.desc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
sortByValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'value', Sort.asc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
sortByValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'value', Sort.desc);
});
}
}
extension EncryptedStringValueQuerySortThenBy
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QSortThenBy> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
thenById() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.asc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
thenByIdDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'id', Sort.desc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
thenByKey() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'key', Sort.asc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
thenByKeyDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'key', Sort.desc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
thenByValue() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'value', Sort.asc);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QAfterSortBy>
thenByValueDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'value', Sort.desc);
});
}
}
extension EncryptedStringValueQueryWhereDistinct
on QueryBuilder<EncryptedStringValue, EncryptedStringValue, QDistinct> {
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QDistinct>
distinctByKey({bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'key', caseSensitive: caseSensitive);
});
}
QueryBuilder<EncryptedStringValue, EncryptedStringValue, QDistinct>
distinctByValue({bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'value', caseSensitive: caseSensitive);
});
}
}
extension EncryptedStringValueQueryProperty on QueryBuilder<
EncryptedStringValue, EncryptedStringValue, QQueryProperty> {
QueryBuilder<EncryptedStringValue, int, QQueryOperations> idProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'id');
});
}
QueryBuilder<EncryptedStringValue, String, QQueryOperations> keyProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'key');
});
}
QueryBuilder<EncryptedStringValue, String, QQueryOperations> valueProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'value');
});
}
}

View file

@ -65,8 +65,7 @@ class NodeModel {
}
/// convenience getter to retrieve login password
Future<String?> getPassword(
FlutterSecureStorageInterface secureStorage) async {
Future<String?> getPassword(SecureStorageInterface secureStorage) async {
return await secureStorage.read(key: "${id}_nodePW");
}

View file

@ -4,6 +4,8 @@ import 'package:stackwallet/models/notification_model.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -20,22 +22,33 @@ class NotificationCard extends StatelessWidget {
return Format.extractDateFrom(date.millisecondsSinceEpoch ~/ 1000);
}
static const double mobileIconSize = 24;
static const double desktopIconSize = 30;
@override
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
return Stack(
children: [
RoundedWhiteContainer(
padding: isDesktop
? const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
)
: const EdgeInsets.all(12),
child: Row(
children: [
notification.changeNowId == null
? SvgPicture.asset(
notification.iconAssetName,
width: 24,
height: 24,
width: isDesktop ? desktopIconSize : mobileIconSize,
height: isDesktop ? desktopIconSize : mobileIconSize,
)
: Container(
width: 24,
height: 24,
width: isDesktop ? desktopIconSize : mobileIconSize,
height: isDesktop ? desktopIconSize : mobileIconSize,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(24),
@ -45,8 +58,8 @@ class NotificationCard extends StatelessWidget {
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
width: 24,
height: 24,
width: isDesktop ? desktopIconSize : mobileIconSize,
height: isDesktop ? desktopIconSize : mobileIconSize,
),
),
const SizedBox(
@ -56,9 +69,35 @@ class NotificationCard extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
notification.title,
style: STextStyles.titleBold12(context),
ConditionalParent(
condition: isDesktop && !notification.read,
builder: (child) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
child,
Text(
"New",
style:
STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen,
),
)
],
),
child: Text(
notification.title,
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
)
: STextStyles.titleBold12(context),
),
),
const SizedBox(
height: 2,
@ -68,11 +107,25 @@ class NotificationCard extends StatelessWidget {
children: [
Text(
notification.description,
style: STextStyles.label(context),
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.label(context),
),
Text(
extractPrettyDateString(notification.date),
style: STextStyles.label(context),
style: isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
)
: STextStyles.label(context),
),
],
),

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/coins/manager.dart';
@ -241,6 +242,7 @@ class _NewWalletRecoveryPhraseWarningViewState
coin,
walletId,
walletName,
ref.read(secureStoreProvider),
node,
txTracker,
ref.read(prefsChangeNotifierProvider),

View file

@ -19,6 +19,7 @@ import 'package:stackwallet/pages/add_wallet_views/restore_wallet_view/sub_widge
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/coins/manager.dart';
@ -273,6 +274,7 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
widget.coin,
walletId,
widget.walletName,
ref.read(secureStoreProvider),
node,
txTracker,
ref.read(prefsChangeNotifierProvider),

View file

@ -15,15 +15,17 @@ import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/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/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/emoji_select_sheet.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:stackwallet/utilities/util.dart';
class AddAddressBookEntryView extends ConsumerStatefulWidget {
const AddAddressBookEntryView({
Key? key,
@ -108,395 +110,537 @@ class _AddAddressBookEntryViewState
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
title: Text(
"New contact",
style: STextStyles.navBarTitle(context),
),
actions: [
Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
right: 10,
),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
key: const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context).extension<StackColors>()!.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
),
onPressed: () {
setState(() {
_isFavorite = !_isFavorite;
});
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
return Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
),
],
),
body: LayoutBuilder(
builder: (context, constraint) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: SingleChildScrollView(
controller: scrollController,
padding: const EdgeInsets.only(
// top: 8,
left: 4,
right: 4,
bottom: 16,
title: Text(
"New contact",
style: STextStyles.navBarTitle(context),
),
child: ConstrainedBox(
constraints: BoxConstraints(
// subtract top and bottom padding set in parent
minHeight: constraint.maxHeight - 16, // - 8,
),
child: IntrinsicHeight(
child: Column(
children: [
const SizedBox(
height: 4,
actions: [
Padding(
padding: const EdgeInsets.only(
top: 10,
bottom: 10,
right: 10,
),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
key: const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context)
.extension<StackColors>()!
.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
),
GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
onPressed: () {
setState(() {
_isFavorite = !_isFavorite;
});
},
),
),
),
],
),
body: child);
},
child: ConditionalParent(
condition: isDesktop,
builder: (child) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Row(
children: [
Text(
"New contact",
style: STextStyles.desktopH3(context),
textAlign: TextAlign.center,
),
const SizedBox(width: 10),
AppBarIconButton(
key:
const Key("addAddressBookEntryFavoriteButtonKey"),
size: 36,
shadows: const [],
color: Theme.of(context)
.extension<StackColors>()!
.background,
icon: SvgPicture.asset(
Assets.svg.star,
color: _isFavorite
? Theme.of(context)
.extension<StackColors>()!
.favoriteStarActive
: Theme.of(context)
.extension<StackColors>()!
.favoriteStarInactive,
width: 20,
height: 20,
),
onPressed: () {
setState(() {
_selectedEmoji = null;
_isFavorite = !_isFavorite;
});
return;
}
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
},
),
],
),
),
const DesktopDialogCloseButton(),
],
),
Expanded(child: child),
],
);
},
child: LayoutBuilder(
builder: (context, constraint) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: SingleChildScrollView(
controller: scrollController,
padding: const EdgeInsets.only(
// top: 8,
left: 4,
right: 4,
bottom: 16,
),
child: ConstrainedBox(
constraints: BoxConstraints(
// subtract top and bottom padding set in parent
minHeight: constraint.maxHeight - 16, // - 8,
),
child: IntrinsicHeight(
child: Column(
children: [
const SizedBox(
height: 4,
),
GestureDetector(
onTap: () {
if (_selectedEmoji != null) {
setState(() {
_selectedEmoji = value;
_selectedEmoji = null;
});
return;
}
});
},
child: SizedBox(
height: 48,
width: 48,
child: Stack(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.user,
height: 24,
width: 24,
)
: Text(
_selectedEmoji!.char,
style:
STextStyles.pageTitleH1(context),
///TODO if desktop make dialog
!isDesktop
? showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => const EmojiSelectSheet(),
).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
})
: showDialog<dynamic>(
context: context,
builder: (context) {
return DesktopDialog(
maxHeight: 700,
maxWidth: 700,
child: Column(
children: [
Row(
children: [
Padding(
padding:
const EdgeInsets.all(32),
child: Text(
"Select emoji",
style:
STextStyles.desktopH3(
context),
textAlign: TextAlign.center,
),
),
],
),
Expanded(
child: LayoutBuilder(
builder:
(context, constraints) {
return SingleChildScrollView(
scrollDirection:
Axis.vertical,
child: ConstrainedBox(
constraints:
BoxConstraints(
minHeight: constraints
.maxHeight,
minWidth: constraints
.maxWidth,
),
child: IntrinsicHeight(
child: Column(
children: const [
Padding(
padding: EdgeInsets
.symmetric(
horizontal:
32),
// child:
// EmojiSelectSheet(),
),
],
),
),
),
);
},
),
),
],
),
),
),
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 14,
width: 14,
);
}).then((value) {
if (value is Emoji) {
setState(() {
_selectedEmoji = value;
});
}
});
},
child: SizedBox(
height: 48,
width: 48,
child: Stack(
children: [
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
borderRadius: BorderRadius.circular(24),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldActiveBG,
),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.plus,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 12,
height: 12,
Assets.svg.user,
height: 24,
width: 24,
)
: SvgPicture.asset(
Assets.svg.thickX,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 8,
height: 8,
: Text(
_selectedEmoji!.char,
style: STextStyles.pageTitleH1(
context),
),
),
),
)
],
),
),
),
const SizedBox(
height: 8,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: nameController,
focusNode: nameFocusNode,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Enter contact name",
nameFocusNode,
context,
).copyWith(
suffixIcon: ref
.read(contactNameIsNotEmptyStateProvider
.state)
.state
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
nameController.text = "";
});
},
),
],
),
Align(
alignment: Alignment.bottomRight,
child: Container(
height: 14,
width: 14,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
child: Center(
child: _selectedEmoji == null
? SvgPicture.asset(
Assets.svg.plus,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 12,
height: 12,
)
: SvgPicture.asset(
Assets.svg.thickX,
color: Theme.of(context)
.extension<StackColors>()!
.textWhite,
width: 8,
height: 8,
),
),
)
: null,
),
)
],
),
),
onChanged: (newValue) {
ref
.read(contactNameIsNotEmptyStateProvider.state)
.state = newValue.isNotEmpty;
},
),
),
if (forms.length <= 1)
const SizedBox(
height: 8,
),
if (forms.length <= 1) forms[0],
if (forms.length > 1)
for (int i = 0; i < forms.length; i++)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 12,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Address ${i + 1}",
style: STextStyles.smallMed12(context),
),
BlueTextButton(
onTap: () {
_removeForm(forms[i].id);
},
text: "Remove",
),
],
),
const SizedBox(
height: 8,
),
forms[i],
],
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
const SizedBox(
height: 16,
),
BlueTextButton(
onTap: () {
_addForm();
scrollController.animateTo(
scrollController.position.maxScrollExtent + 500,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
text: "+ Add another address",
),
// GestureDetector(
//
// child: Text(
// "+ Add another address",
// style: STextStyles.largeMedium14(context),
// ),
// ),
const SizedBox(
height: 16,
),
const Spacer(),
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
child: TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
controller: nameController,
focusNode: nameFocusNode,
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Enter contact name",
nameFocusNode,
context,
).copyWith(
suffixIcon: ref
.read(contactNameIsNotEmptyStateProvider
.state)
.state
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
nameController.text = "";
});
},
),
],
),
),
)
: null,
),
onChanged: (newValue) {
ref
.read(
contactNameIsNotEmptyStateProvider.state)
.state = newValue.isNotEmpty;
},
),
),
if (forms.length <= 1)
const SizedBox(
width: 16,
height: 8,
),
Expanded(
child: Builder(
builder: (context) {
bool nameExists = ref
.watch(contactNameIsNotEmptyStateProvider
.state)
.state;
bool validForms = ref.watch(
validContactStateProvider(forms
.map((e) => e.id)
.toList(growable: false)));
bool shouldEnableSave =
validForms && nameExists;
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75),
);
}
List<ContactAddressEntry> entries =
[];
for (int i = 0;
i < forms.length;
i++) {
entries.add(ref
.read(addressEntryDataProvider(
forms[i].id))
.buildAddressEntry());
}
Contact contact = Contact(
emojiChar: _selectedEmoji?.char,
name: nameController.text,
addresses: entries,
isFavorite: _isFavorite,
);
if (await ref
.read(addressBookServiceProvider)
.addContact(contact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
: null,
child: Text(
"Save",
style: STextStyles.button(context).copyWith(
color: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary
: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimaryDisabled,
if (forms.length <= 1) forms[0],
if (forms.length > 1)
for (int i = 0; i < forms.length; i++)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 12,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Address ${i + 1}",
style: STextStyles.smallMed12(context),
),
),
);
},
BlueTextButton(
onTap: () {
_removeForm(forms[i].id);
},
text: "Remove",
),
],
),
const SizedBox(
height: 8,
),
forms[i],
],
),
),
],
),
],
const SizedBox(
height: 16,
),
BlueTextButton(
onTap: () {
_addForm();
scrollController.animateTo(
scrollController.position.maxScrollExtent + 500,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
text: "+ Add another address",
),
// GestureDetector(
//
// child: Text(
// "+ Add another address",
// style: STextStyles.largeMedium14(context),
// ),
// ),
const SizedBox(
height: 16,
),
const Spacer(),
Row(
children: [
Expanded(
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Cancel",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () async {
if (FocusScope.of(context).hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 75));
}
if (mounted) {
Navigator.of(context).pop();
}
},
),
),
const SizedBox(
width: 16,
),
Expanded(
child: Builder(
builder: (context) {
bool nameExists = ref
.watch(contactNameIsNotEmptyStateProvider
.state)
.state;
bool validForms = ref.watch(
validContactStateProvider(forms
.map((e) => e.id)
.toList(growable: false)));
bool shouldEnableSave =
validForms && nameExists;
return TextButton(
style: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(
context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(
context),
onPressed: shouldEnableSave
? () async {
if (FocusScope.of(context)
.hasFocus) {
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(
milliseconds: 75),
);
}
List<ContactAddressEntry> entries =
[];
for (int i = 0;
i < forms.length;
i++) {
entries.add(ref
.read(
addressEntryDataProvider(
forms[i].id))
.buildAddressEntry());
}
Contact contact = Contact(
emojiChar: _selectedEmoji?.char,
name: nameController.text,
addresses: entries,
isFavorite: _isFavorite,
);
if (await ref
.read(
addressBookServiceProvider)
.addContact(contact)) {
if (mounted) {
Navigator.of(context).pop();
}
// TODO show success notification
} else {
// TODO show error notification
}
}
: null,
child: Text(
"Save",
style:
STextStyles.button(context).copyWith(
color: shouldEnableSave
? Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary
: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimaryDisabled,
),
),
);
},
),
),
],
),
],
),
),
),
),
),
);
},
);
},
),
),
);
}

View file

@ -5,7 +5,12 @@ import 'package:stackwallet/providers/ui/address_book_providers/address_book_fil
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class AddressBookFilterView extends ConsumerStatefulWidget {
@ -41,167 +46,224 @@ class _AddressBookFilterViewState extends ConsumerState<AddressBookFilterView> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
leading: AppBarBackButton(
onPressed: () async {
Navigator.of(context).pop();
},
),
title: Text(
"Filter addresses",
style: STextStyles.navBarTitle(context),
),
),
body: Padding(
padding: const EdgeInsets.all(12),
child: LayoutBuilder(builder: (builderContext, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
final isDesktop = Util.isDesktop;
return ConditionalParent(
condition: !isDesktop,
builder: (child) {
return Scaffold(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
appBar: AppBar(
backgroundColor:
Theme.of(context).extension<StackColors>()!.background,
leading: AppBarBackButton(
onPressed: () async {
Navigator.of(context).pop();
},
),
title: Text(
"Filter addresses",
style: STextStyles.navBarTitle(context),
),
),
body: Padding(
padding: const EdgeInsets.all(12),
child: LayoutBuilder(builder: (builderContext, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
RoundedWhiteContainer(
child: Text(
"Only selected cryptocurrency addresses will be displayed.",
style: STextStyles.itemSubtitle(context),
),
),
const SizedBox(
height: 12,
),
Text(
"Select cryptocurrency",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
child,
],
),
),
),
),
);
}),
),
);
},
child: ConditionalParent(
condition: isDesktop,
builder: (child) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.all(32),
child: Text(
"Select cryptocurrency",
style: STextStyles.desktopH3(context),
textAlign: TextAlign.center,
),
),
const DesktopDialogCloseButton(),
],
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
RoundedWhiteContainer(
child: Text(
"Only selected cryptocurrency addresses will be displayed.",
style: STextStyles.itemSubtitle(context),
Expanded(
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 32),
child: child,
),
],
),
),
),
const SizedBox(
height: 12,
),
Text(
"Select cryptocurrency",
style: STextStyles.smallMed12(context),
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Wrap(
children: [
..._coins.map(
(coin) => Row(
children: [
GestureDetector(
onTap: () {
if (ref
.read(addressBookFilterProvider)
.coins
.contains(coin)) {
ref
.read(addressBookFilterProvider)
.remove(coin, true);
} else {
);
},
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 32, vertical: 32),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SecondaryButton(
width: 248,
desktopMed: true,
enabled: true,
label: "Cancel",
onPressed: () {
Navigator.of(context).pop();
},
),
// const SizedBox(width: 16),
PrimaryButton(
width: 248,
desktopMed: true,
enabled: true,
label: "Apply",
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
),
],
);
},
child: RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Wrap(
children: [
..._coins.map(
(coin) => Row(
children: [
GestureDetector(
onTap: () {
if (ref
.read(addressBookFilterProvider)
.coins
.contains(coin)) {
ref
.read(addressBookFilterProvider)
.remove(coin, true);
} else {
ref.read(addressBookFilterProvider).add(coin, true);
}
setState(() {});
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 20,
width: 20,
child: Checkbox(
value: ref
.watch(addressBookFilterProvider
.select((value) => value.coins))
.contains(coin),
onChanged: (value) {
if (value is bool) {
if (value) {
ref
.read(addressBookFilterProvider)
.add(coin, true);
} else {
ref
.read(addressBookFilterProvider)
.remove(coin, true);
}
setState(() {});
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
SizedBox(
height: 20,
width: 20,
child: Checkbox(
value: ref
.watch(
addressBookFilterProvider
.select((value) =>
value.coins))
.contains(coin),
onChanged: (value) {
if (value is bool) {
if (value) {
ref
.read(
addressBookFilterProvider)
.add(coin, true);
} else {
ref
.read(
addressBookFilterProvider)
.remove(coin, true);
}
setState(() {});
}
},
),
),
const SizedBox(
width: 12,
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
coin.prettyName,
style:
STextStyles.largeMedium14(
context),
),
const SizedBox(
height: 2,
),
Text(
coin.ticker,
style:
STextStyles.itemSubtitle(
context),
),
],
)
],
),
),
),
}
},
),
),
const SizedBox(
width: 12,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
coin.prettyName,
style: STextStyles.largeMedium14(context),
),
const SizedBox(
height: 2,
),
Text(
coin.ticker,
style: STextStyles.itemSubtitle(context),
),
],
),
),
],
)
],
),
),
),
const Spacer(),
// Row(
// children: [
// TextButton(
// onPressed: () {},
// child: Text("Cancel"),
// ),
// SizedBox(
// width: 16,
// ),
// TextButton(
// onPressed: () {},
// child: Text("Cancel"),
// ),
// ],
// )
],
),
),
],
),
),
),
);
}),
],
),
),
),
);
}

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,10 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/biometrics.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -20,15 +20,11 @@ class CreatePinView extends ConsumerStatefulWidget {
const CreatePinView({
Key? key,
this.popOnSuccess = false,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
this.biometrics = const Biometrics(),
}) : super(key: key);
static const String routeName = "/createPin";
final FlutterSecureStorageInterface secureStore;
final Biometrics biometrics;
final bool popOnSuccess;
@ -58,12 +54,12 @@ class _CreatePinViewState extends ConsumerState<CreatePinView> {
final TextEditingController _pinPutController2 = TextEditingController();
final FocusNode _pinPutFocusNode2 = FocusNode();
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late Biometrics biometrics;
@override
initState() {
_secureStore = widget.secureStore;
_secureStore = ref.read(secureStoreProvider);
biometrics = widget.biometrics;
super.initState();
}

View file

@ -2,12 +2,12 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
// import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
// import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -33,9 +33,6 @@ class LockscreenView extends ConsumerStatefulWidget {
this.popOnSuccess = false,
this.isInitialAppLogin = false,
this.routeOnSuccessArguments,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
this.biometrics = const Biometrics(),
this.onSuccess,
}) : super(key: key);
@ -50,7 +47,6 @@ class LockscreenView extends ConsumerStatefulWidget {
final String biometricsAuthenticationTitle;
final String biometricsLocalizedReason;
final String biometricsCancelButtonString;
final FlutterSecureStorageInterface secureStore;
final Biometrics biometrics;
final VoidCallback? onSuccess;
@ -134,7 +130,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
void initState() {
_shakeController = ShakeController();
_secureStore = widget.secureStore;
_secureStore = ref.read(secureStoreProvider);
biometrics = widget.biometrics;
_attempts = 0;
_timeout = Duration.zero;
@ -162,7 +158,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
final _pinTextController = TextEditingController();
final FocusNode _pinFocusNode = FocusNode();
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late Biometrics biometrics;
Scaffold get _body => Scaffold(

View file

@ -8,6 +8,7 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/route_generator.dart';
@ -23,6 +24,8 @@ 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/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -54,22 +57,17 @@ class _ConfirmTransactionViewState
late final String routeOnSuccessName;
late final bool isDesktop;
int _fee = 12;
final List<int> _dropDownItems = [
12,
22,
234,
];
Future<void> _attemptSend(BuildContext context) async {
unawaited(showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return const SendingTransactionDialog();
},
));
unawaited(
showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: false,
builder: (context) {
return const SendingTransactionDialog();
},
),
);
final note = transactionInfo["note"] as String? ?? "";
final manager =
@ -122,25 +120,66 @@ class _ConfirmTransactionViewState
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return StackDialog(
title: "Broadcast transaction failed",
message: e.toString(),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Ok",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
if (isDesktop) {
return DesktopDialog(
maxWidth: 450,
child: Padding(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Broadcast transaction failed",
style: STextStyles.desktopH3(context),
),
const SizedBox(
height: 24,
),
Text(
e.toString(),
style: STextStyles.smallMed14(context),
),
const SizedBox(
height: 56,
),
Row(
children: [
const Spacer(),
Expanded(
child: PrimaryButton(
desktopMed: true,
label: "Ok",
onPressed: Navigator.of(context).pop,
),
),
],
)
],
),
),
onPressed: () {
Navigator.of(context).pop();
},
),
);
);
} else {
return StackDialog(
title: "Broadcast transaction failed",
message: e.toString(),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Ok",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () {
Navigator.of(context).pop();
},
),
);
}
},
);
}
@ -568,32 +607,101 @@ class _ConfirmTransactionViewState
),
if (isDesktop)
Padding(
padding: const EdgeInsets.only(
top: 10,
left: 32,
right: 32,
),
child: DropdownButtonFormField(
value: _fee,
items: _dropDownItems
.map(
(e) => DropdownMenuItem(
value: e,
child: Text(
e.toString(),
),
),
)
.toList(),
onChanged: (value) {
if (value is int) {
setState(() {
_fee = value;
});
}
},
),
),
padding: const EdgeInsets.only(
top: 10,
left: 32,
right: 32,
),
child: RoundedContainer(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 18,
),
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
child: Builder(builder: (context) {
final coin = ref
.watch(walletsChangeNotifierProvider
.select((value) => value.getManager(walletId)))
.coin;
final fee = Format.satoshisToAmount(
transactionInfo["fee"] as int,
coin: coin,
);
return Text(
"${Format.localizedStringAsFixed(
value: fee,
locale: ref.watch(localeServiceChangeNotifierProvider
.select((value) => value.locale)),
decimalPlaces: coin == Coin.monero
? Constants.decimalPlacesMonero
: coin == Coin.wownero
? Constants.decimalPlacesWownero
: Constants.decimalPlaces,
)} ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
);
}),
)
// DropdownButtonHideUnderline(
// child: DropdownButton2(
// offset: const Offset(0, -10),
// isExpanded: true,
//
// dropdownElevation: 0,
// value: _fee,
// items: [
// ..._dropDownItems.map(
// (e) {
// String message = _fee.toString();
//
// return DropdownMenuItem(
// value: e,
// child: Text(message),
// );
// },
// ),
// ],
// onChanged: (value) {
// if (value is int) {
// setState(() {
// _fee = value;
// });
// }
// },
// icon: SvgPicture.asset(
// Assets.svg.chevronDown,
// width: 12,
// height: 6,
// color:
// Theme.of(context).extension<StackColors>()!.textDark3,
// ),
// buttonPadding: const EdgeInsets.symmetric(
// horizontal: 16,
// vertical: 8,
// ),
// buttonDecoration: BoxDecoration(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG,
// borderRadius: BorderRadius.circular(
// Constants.size.circularBorderRadius,
// ),
// ),
// dropdownDecoration: BoxDecoration(
// color: Theme.of(context)
// .extension<StackColors>()!
// .textFieldDefaultBG,
// borderRadius: BorderRadius.circular(
// Constants.size.circularBorderRadius,
// ),
// ),
// ),
// ),
),
if (!isDesktop) const Spacer(),
SizedBox(
height: isDesktop ? 23 : 12,
@ -674,25 +782,56 @@ class _ConfirmTransactionViewState
label: "Send",
desktopMed: true,
onPressed: () async {
final unlocked = await Navigator.push(
context,
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
builder: (_) => const LockscreenView(
showBackButton: true,
popOnSuccess: true,
routeOnSuccessArguments: true,
routeOnSuccess: "",
biometricsCancelButtonString: "CANCEL",
biometricsLocalizedReason:
"Authenticate to send transaction",
biometricsAuthenticationTitle: "Confirm Transaction",
final dynamic unlocked;
if (isDesktop) {
unlocked = await showDialog<bool?>(
context: context,
builder: (context) => DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
DesktopDialogCloseButton(),
],
),
const Padding(
padding: EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: DesktopAuthSend(),
),
],
),
),
settings:
const RouteSettings(name: "/confirmsendlockscreen"),
),
);
);
} else {
unlocked = await Navigator.push(
context,
RouteGenerator.getRoute(
shouldUseMaterialRoute:
RouteGenerator.useMaterialPageRoute,
builder: (_) => const LockscreenView(
showBackButton: true,
popOnSuccess: true,
routeOnSuccessArguments: true,
routeOnSuccess: "",
biometricsCancelButtonString: "CANCEL",
biometricsLocalizedReason:
"Authenticate to send transaction",
biometricsAuthenticationTitle: "Confirm Transaction",
),
settings:
const RouteSettings(name: "/confirmsendlockscreen"),
),
);
}
if (unlocked is bool && unlocked && mounted) {
unawaited(_attemptSend(context));

View file

@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class SendingTransactionDialog extends StatefulWidget {
@ -43,24 +46,56 @@ class _RestoringDialogState extends State<SendingTransactionDialog>
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: StackDialog(
title: "Sending transaction",
// // TODO get message from design team
// message: "<PLACEHOLDER>",
icon: RotationTransition(
turns: _spinAnimation,
child: SvgPicture.asset(
Assets.svg.arrowRotate,
color: Theme.of(context).extension<StackColors>()!.accentColorDark,
width: 24,
height: 24,
if (Util.isDesktop) {
return DesktopDialog(
child: Padding(
padding: const EdgeInsets.all(40),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Sending transaction",
style: STextStyles.desktopH3(context),
),
const SizedBox(
height: 40,
),
RotationTransition(
turns: _spinAnimation,
child: SvgPicture.asset(
Assets.svg.arrowRotate,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
width: 24,
height: 24,
),
),
],
),
),
),
);
);
} else {
return WillPopScope(
onWillPop: () async {
return false;
},
child: StackDialog(
title: "Sending transaction",
// // TODO get message from design team
// message: "<PLACEHOLDER>",
icon: RotationTransition(
turns: _spinAnimation,
child: SvgPicture.asset(
Assets.svg.arrowRotate,
color:
Theme.of(context).extension<StackColors>()!.accentColorDark,
width: 24,
height: 24,
),
),
),
);
}
}
}

View file

@ -7,19 +7,25 @@ import 'package:event_bus/event_bus.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:stackwallet/models/isar/models/log.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
import 'package:stackwallet/providers/global/debug_service_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.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/stack_file_system.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/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
@ -28,15 +34,6 @@ import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/stack_file_system.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/util.dart';
class DebugView extends ConsumerStatefulWidget {
const DebugView({Key? key}) : super(key: key);
@ -352,10 +349,10 @@ class _DebugViewState extends ConsumerState<DebugView> {
BlueTextButton(
text: "Save logs to file",
onTap: () async {
final systemfile = StackFileSystem();
final systemfile = SWBFileSystem();
await systemfile.prepareStorage();
Directory rootPath =
(await getApplicationDocumentsDirectory());
Directory rootPath = await StackFileSystem
.applicationRootDirectory();
if (Platform.isAndroid) {
rootPath = Directory("/storage/emulated/0/");

View file

@ -3,11 +3,11 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -40,9 +40,6 @@ class AddEditNodeView extends ConsumerStatefulWidget {
required this.coin,
required this.nodeId,
required this.routeOnSuccessOrDelete,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
}) : super(key: key);
static const String routeName = "/addEditNode";
@ -51,7 +48,6 @@ class AddEditNodeView extends ConsumerStatefulWidget {
final Coin coin;
final String routeOnSuccessOrDelete;
final String? nodeId;
final FlutterSecureStorageInterface secureStore;
@override
ConsumerState<AddEditNodeView> createState() => _AddEditNodeViewState();
@ -533,7 +529,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
children: [
NodeForm(
node: node,
secureStore: widget.secureStore,
secureStore: ref.read(secureStoreProvider),
readOnly: false,
coin: widget.coin,
onChanged: (canSave, canTest) {
@ -638,7 +634,7 @@ class NodeForm extends ConsumerStatefulWidget {
}) : super(key: key);
final NodeModel? node;
final FlutterSecureStorageInterface secureStore;
final SecureStorageInterface secureStore;
final bool readOnly;
final Coin coin;
final void Function(bool canSave, bool canTestConnection)? onChanged;

View file

@ -2,11 +2,11 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -32,14 +32,10 @@ class NodeDetailsView extends ConsumerStatefulWidget {
required this.coin,
required this.nodeId,
required this.popRouteName,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
}) : super(key: key);
static const String routeName = "/nodeDetails";
final FlutterSecureStorageInterface secureStore;
final Coin coin;
final String nodeId;
final String popRouteName;
@ -49,7 +45,7 @@ class NodeDetailsView extends ConsumerStatefulWidget {
}
class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
late final FlutterSecureStorageInterface secureStore;
late final SecureStorageInterface secureStore;
late final Coin coin;
late final String nodeId;
late final String popRouteName;
@ -58,7 +54,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
@override
initState() {
secureStore = widget.secureStore;
secureStore = ref.read(secureStoreProvider);
coin = widget.coin;
nodeId = widget.nodeId;
popRouteName = widget.popRouteName;

View file

@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
@ -11,23 +12,18 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
class ChangePinView extends StatefulWidget {
class ChangePinView extends ConsumerStatefulWidget {
const ChangePinView({
Key? key,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
}) : super(key: key);
static const String routeName = "/changePin";
final FlutterSecureStorageInterface secureStore;
@override
State<ChangePinView> createState() => _ChangePinViewState();
ConsumerState<ChangePinView> createState() => _ChangePinViewState();
}
class _ChangePinViewState extends State<ChangePinView> {
class _ChangePinViewState extends ConsumerState<ChangePinView> {
BoxDecoration get _pinPutDecoration {
return BoxDecoration(
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
@ -49,11 +45,11 @@ class _ChangePinViewState extends State<ChangePinView> {
final TextEditingController _pinPutController2 = TextEditingController();
final FocusNode _pinPutFocusNode2 = FocusNode();
late final FlutterSecureStorageInterface _secureStore;
late final SecureStorageInterface _secureStore;
@override
void initState() {
_secureStore = widget.secureStore;
_secureStore = ref.read(secureStoreProvider);
super.initState();
}

View file

@ -4,15 +4,15 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/auto_backup_view.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/stack_file_system.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/backup_frequency_type_select_sheet.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
@ -21,33 +21,27 @@ import 'package:stackwallet/utilities/format.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/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/progress_bar.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:zxcvbn/zxcvbn.dart';
import 'package:stackwallet/utilities/util.dart';
class CreateAutoBackupView extends ConsumerStatefulWidget {
const CreateAutoBackupView({
Key? key,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
}) : super(key: key);
static const String routeName = "/createAutoBackup";
final FlutterSecureStorageInterface secureStore;
@override
ConsumerState<CreateAutoBackupView> createState() =>
_EnableAutoBackupViewState();
}
class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
late final FlutterSecureStorageInterface secureStore;
late final SecureStorageInterface secureStore;
late final TextEditingController fileLocationController;
late final TextEditingController passwordController;
@ -55,7 +49,7 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
late final FocusNode passwordFocusNode;
late final FocusNode passwordRepeatFocusNode;
late final StackFileSystem stackFileSystem;
late final SWBFileSystem stackFileSystem;
final zxcvbn = Zxcvbn();
String passwordFeedback =
@ -75,8 +69,8 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
@override
void initState() {
secureStore = widget.secureStore;
stackFileSystem = StackFileSystem();
secureStore = ref.read(secureStoreProvider);
stackFileSystem = SWBFileSystem();
fileLocationController = TextEditingController();
passwordController = TextEditingController();
passwordRepeatController = TextEditingController();
@ -585,7 +579,9 @@ class _EnableAutoBackupViewState extends ConsumerState<CreateAutoBackupView> {
final String fileToSave =
createAutoBackupFilename(pathToSave, now);
final backup = await SWB.createStackWalletJSON();
final backup = await SWB.createStackWalletJSON(
secureStorage: secureStore,
);
bool result = await SWB.encryptStackWalletWithADK(
fileToSave,

View file

@ -7,7 +7,8 @@ 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/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/stack_file_system.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
@ -40,7 +41,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
late final FocusNode passwordFocusNode;
late final FocusNode passwordRepeatFocusNode;
late final StackFileSystem stackFileSystem;
late final SWBFileSystem stackFileSystem;
final zxcvbn = Zxcvbn();
String passwordFeedback =
@ -60,7 +61,7 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
@override
void initState() {
stackFileSystem = StackFileSystem();
stackFileSystem = SWBFileSystem();
fileLocationController = TextEditingController();
passwordController = TextEditingController();
passwordRepeatController = TextEditingController();
@ -443,222 +444,229 @@ class _RestoreFromFileViewState extends State<CreateBackupView> {
),
if (!isDesktop) const Spacer(),
!isDesktop
? TextButton(
style: shouldEnableCreate
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(context),
onPressed: !shouldEnableCreate
? null
: () async {
final String pathToSave =
fileLocationController.text;
final String passphrase = passwordController.text;
final String repeatPassphrase =
passwordRepeatController.text;
? Consumer(builder: (context, ref, __) {
return TextButton(
style: shouldEnableCreate
? Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context)
: Theme.of(context)
.extension<StackColors>()!
.getPrimaryDisabledButtonColor(context),
onPressed: !shouldEnableCreate
? null
: () async {
final String pathToSave =
fileLocationController.text;
final String passphrase = passwordController.text;
final String repeatPassphrase =
passwordRepeatController.text;
if (pathToSave.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory not chosen",
context: context,
));
return;
}
if (!(await Directory(pathToSave).exists())) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory does not exist",
context: context,
));
return;
}
if (passphrase.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "A passphrase is required",
context: context,
));
return;
}
if (passphrase != repeatPassphrase) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase does not match",
context: context,
));
return;
}
unawaited(showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackDialog(
title: "Encrypting backup",
message: "This shouldn't take long",
),
));
// make sure the dialog is able to be displayed for at least 1 second
await Future<void>.delayed(
const Duration(seconds: 1));
final DateTime now = DateTime.now();
final String fileToSave =
"$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb";
final backup = await SWB.createStackWalletJSON();
bool result =
await SWB.encryptStackWalletWithPassphrase(
fileToSave,
passphrase,
jsonEncode(backup),
);
if (mounted) {
// pop encryption progress dialog
Navigator.of(context).pop();
if (result) {
await showDialog<dynamic>(
if (pathToSave.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory not chosen",
context: context,
barrierDismissible: false,
builder: (_) => Platform.isAndroid
? StackOkDialog(
title: "Backup saved to:",
message: fileToSave,
)
: const StackOkDialog(
title: "Backup creation succeeded"),
);
passwordController.text = "";
passwordRepeatController.text = "";
setState(() {});
} else {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackOkDialog(
title: "Backup creation failed"),
);
));
return;
}
}
},
child: Text(
"Create backup",
style: STextStyles.button(context),
),
)
if (!(await Directory(pathToSave).exists())) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory does not exist",
context: context,
));
return;
}
if (passphrase.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "A passphrase is required",
context: context,
));
return;
}
if (passphrase != repeatPassphrase) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase does not match",
context: context,
));
return;
}
unawaited(showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackDialog(
title: "Encrypting backup",
message: "This shouldn't take long",
),
));
// make sure the dialog is able to be displayed for at least 1 second
await Future<void>.delayed(
const Duration(seconds: 1));
final DateTime now = DateTime.now();
final String fileToSave =
"$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb";
final backup = await SWB.createStackWalletJSON(
secureStorage: ref.read(secureStoreProvider));
bool result =
await SWB.encryptStackWalletWithPassphrase(
fileToSave,
passphrase,
jsonEncode(backup),
);
if (mounted) {
// pop encryption progress dialog
Navigator.of(context).pop();
if (result) {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => Platform.isAndroid
? StackOkDialog(
title: "Backup saved to:",
message: fileToSave,
)
: const StackOkDialog(
title: "Backup creation succeeded"),
);
passwordController.text = "";
passwordRepeatController.text = "";
setState(() {});
} else {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackOkDialog(
title: "Backup creation failed"),
);
}
}
},
child: Text(
"Create backup",
style: STextStyles.button(context),
),
);
})
: Row(
children: [
PrimaryButton(
width: 183,
desktopMed: true,
label: "Create backup",
enabled: shouldEnableCreate,
onPressed: !shouldEnableCreate
? null
: () async {
final String pathToSave =
fileLocationController.text;
final String passphrase =
passwordController.text;
final String repeatPassphrase =
passwordRepeatController.text;
Consumer(builder: (context, ref, __) {
return PrimaryButton(
width: 183,
desktopMed: true,
label: "Create backup",
enabled: shouldEnableCreate,
onPressed: !shouldEnableCreate
? null
: () async {
final String pathToSave =
fileLocationController.text;
final String passphrase =
passwordController.text;
final String repeatPassphrase =
passwordRepeatController.text;
if (pathToSave.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory not chosen",
context: context,
));
return;
}
if (!(await Directory(pathToSave).exists())) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory does not exist",
context: context,
));
return;
}
if (passphrase.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "A passphrase is required",
context: context,
));
return;
}
if (passphrase != repeatPassphrase) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase does not match",
context: context,
));
return;
}
unawaited(showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackDialog(
title: "Encrypting backup",
message: "This shouldn't take long",
),
));
// make sure the dialog is able to be displayed for at least 1 second
await Future<void>.delayed(
const Duration(seconds: 1));
final DateTime now = DateTime.now();
final String fileToSave =
"$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb";
final backup =
await SWB.createStackWalletJSON();
bool result =
await SWB.encryptStackWalletWithPassphrase(
fileToSave,
passphrase,
jsonEncode(backup),
);
if (mounted) {
// pop encryption progress dialog
Navigator.of(context).pop();
if (result) {
await showDialog<dynamic>(
if (pathToSave.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory not chosen",
context: context,
barrierDismissible: false,
builder: (_) => Platform.isAndroid
? StackOkDialog(
title: "Backup saved to:",
message: fileToSave,
)
: const StackOkDialog(
title:
"Backup creation succeeded"),
);
passwordController.text = "";
passwordRepeatController.text = "";
setState(() {});
} else {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackOkDialog(
title: "Backup creation failed"),
);
));
return;
}
}
},
),
if (!(await Directory(pathToSave).exists())) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory does not exist",
context: context,
));
return;
}
if (passphrase.isEmpty) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "A passphrase is required",
context: context,
));
return;
}
if (passphrase != repeatPassphrase) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase does not match",
context: context,
));
return;
}
unawaited(showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackDialog(
title: "Encrypting backup",
message: "This shouldn't take long",
),
));
// make sure the dialog is able to be displayed for at least 1 second
await Future<void>.delayed(
const Duration(seconds: 1));
final DateTime now = DateTime.now();
final String fileToSave =
"$pathToSave/stackbackup_${now.year}_${now.month}_${now.day}_${now.hour}_${now.minute}_${now.second}.swb";
final backup =
await SWB.createStackWalletJSON(
secureStorage:
ref.read(secureStoreProvider));
bool result = await SWB
.encryptStackWalletWithPassphrase(
fileToSave,
passphrase,
jsonEncode(backup),
);
if (mounted) {
// pop encryption progress dialog
Navigator.of(context).pop();
if (result) {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => Platform.isAndroid
? StackOkDialog(
title: "Backup saved to:",
message: fileToSave,
)
: const StackOkDialog(
title:
"Backup creation succeeded"),
);
passwordController.text = "";
passwordRepeatController.text = "";
setState(() {});
} else {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackOkDialog(
title: "Backup creation failed"),
);
}
}
},
);
}),
const SizedBox(
width: 16,
),

View file

@ -1,6 +1,11 @@
import 'package:flutter/material.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/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class CancelStackRestoreDialog extends StatelessWidget {
@ -14,38 +19,95 @@ class CancelStackRestoreDialog extends StatelessWidget {
onWillPop: () async {
return false;
},
child: StackDialog(
title: "Cancel restore process",
message:
"Cancelling will revert any changes that may have been applied",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Back",
style: STextStyles.itemSubtitle12(context),
),
onPressed: () {
Navigator.of(context).pop(false);
},
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
child: Text(
"Yes, cancel",
style: STextStyles.itemSubtitle12(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.buttonTextPrimary,
child: !Util.isDesktop
? StackDialog(
title: "Cancel restore process",
message:
"Cancelling will revert any changes that may have been applied",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Back",
style: STextStyles.itemSubtitle12(context),
),
onPressed: () {
Navigator.of(context).pop(false);
},
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
child: Text(
"Yes, cancel",
style: STextStyles.itemSubtitle12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
),
onPressed: () {
Navigator.of(context).pop(true);
},
),
)
: DesktopDialog(
maxHeight: 250,
maxWidth: 600,
child: Padding(
padding: const EdgeInsets.only(
top: 20, left: 32, right: 32, bottom: 20),
child: Column(
children: [
Text(
"Cancel Restore Process",
style: STextStyles.desktopH3(context),
),
const SizedBox(height: 24),
SizedBox(
width: 500,
child: RoundedContainer(
color: Theme.of(context)
.extension<StackColors>()!
.snackBarBackError,
child: Text(
"If you cancel, the restore will not complete, and "
"the wallets will not appear in your Stack.",
style: STextStyles.desktopTextMedium(context),
),
),
),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
width: 248,
desktopMed: true,
enabled: true,
label: "Keep restoring",
onPressed: () {
Navigator.of(context).pop(false);
},
),
const SizedBox(width: 20),
PrimaryButton(
width: 248,
desktopMed: true,
enabled: true,
label: "Cancel anyway",
onPressed: () {
Navigator.of(context).pop(true);
},
)
],
),
],
),
),
),
),
onPressed: () {
Navigator.of(context).pop(true);
},
),
),
);
}
}

View file

@ -3,10 +3,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stack_wallet_backup/stack_wallet_backup.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/models/contact_address_entry.dart';
@ -91,10 +88,13 @@ abstract class SWB {
static bool _shouldCancelRestore = false;
static bool _checkShouldCancel(PreRestoreState? revertToState) {
static bool _checkShouldCancel(
PreRestoreState? revertToState,
SecureStorageInterface secureStorageInterface,
) {
if (_shouldCancelRestore) {
if (revertToState != null) {
_revert(revertToState);
_revert(revertToState, secureStorageInterface);
} else {
_cancelCompleter!.complete();
_shouldCancelRestore = false;
@ -193,15 +193,15 @@ abstract class SWB {
/// [secureStorage] parameter exposed for testing purposes
static Future<Map<String, dynamic>> createStackWalletJSON({
FlutterSecureStorageInterface? secureStorage,
required SecureStorageInterface secureStorage,
}) async {
Logging.instance
.log("Starting createStackWalletJSON...", level: LogLevel.Info);
final _wallets = Wallets.sharedInstance;
Map<String, dynamic> backupJson = {};
NodeService nodeService = NodeService();
final _secureStore =
secureStorage ?? const SecureStorageWrapper(FlutterSecureStorage());
NodeService nodeService =
NodeService(secureStorageInterface: secureStorage);
final _secureStore = secureStorage;
Logging.instance.log("createStackWalletJSON awaiting DB.instance.mutex...",
level: LogLevel.Info);
@ -448,6 +448,7 @@ abstract class SWB {
Map<String, dynamic> validJSON,
StackRestoringUIState? uiState,
Map<String, String> oldToNewWalletIdMap,
SecureStorageInterface secureStorageInterface,
) async {
Map<String, dynamic> prefs = validJSON["prefs"] as Map<String, dynamic>;
List<dynamic>? addressBookEntries =
@ -486,7 +487,11 @@ abstract class SWB {
"SWB restoring nodes",
level: LogLevel.Warning,
);
await _restoreNodes(nodes, primaryNodes);
await _restoreNodes(
nodes,
primaryNodes,
secureStorageInterface,
);
uiState?.nodes = StackRestoringStatus.success;
uiState?.trades = StackRestoringStatus.restoring;
@ -543,6 +548,7 @@ abstract class SWB {
static Future<bool?> restoreStackWalletJSON(
String jsonBackup,
StackRestoringUIState? uiState,
SecureStorageInterface secureStorageInterface,
) async {
if (!Platform.isLinux) await Wakelock.enable();
@ -550,7 +556,8 @@ abstract class SWB {
"SWB creating temp backup",
level: LogLevel.Warning,
);
final preRestoreJSON = await createStackWalletJSON();
final preRestoreJSON =
await createStackWalletJSON(secureStorage: secureStorageInterface);
Logging.instance.log(
"SWB temp backup created",
level: LogLevel.Warning,
@ -587,19 +594,34 @@ abstract class SWB {
// basic cancel check here
// no reverting required yet as nothing has been written to store
if (_checkShouldCancel(null)) {
if (_checkShouldCancel(
null,
secureStorageInterface,
)) {
return false;
}
await _restoreEverythingButWallets(validJSON, uiState, oldToNewWalletIdMap);
await _restoreEverythingButWallets(
validJSON,
uiState,
oldToNewWalletIdMap,
secureStorageInterface,
);
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
final nodeService = NodeService();
final walletsService = WalletsService();
final nodeService = NodeService(
secureStorageInterface: secureStorageInterface,
);
final walletsService = WalletsService(
secureStorageInterface: secureStorageInterface,
);
final _prefs = Prefs.instance;
await _prefs.init();
@ -609,7 +631,10 @@ abstract class SWB {
for (var walletbackup in wallets) {
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
@ -647,7 +672,10 @@ abstract class SWB {
final failovers = nodeService.failoverNodesFor(coin: coin);
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
@ -655,6 +683,7 @@ abstract class SWB {
coin,
walletId,
walletName,
secureStorageInterface,
node,
txTracker,
_prefs,
@ -665,7 +694,10 @@ abstract class SWB {
managers.add(Tuple2(walletbackup, manager));
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
@ -679,7 +711,10 @@ abstract class SWB {
}
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
@ -690,7 +725,10 @@ abstract class SWB {
// start restoring wallets
for (final tuple in managers) {
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
final bools = await asyncRestore(tuple, uiState, walletsService);
@ -698,13 +736,19 @@ abstract class SWB {
}
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
for (Future<bool> status in restoreStatuses) {
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
await status;
@ -712,7 +756,10 @@ abstract class SWB {
if (!Platform.isLinux) await Wakelock.disable();
// check if cancel was requested and restore previous state
if (_checkShouldCancel(preRestoreState)) {
if (_checkShouldCancel(
preRestoreState,
secureStorageInterface,
)) {
return false;
}
@ -720,7 +767,10 @@ abstract class SWB {
return true;
}
static Future<void> _revert(PreRestoreState revertToState) async {
static Future<void> _revert(
PreRestoreState revertToState,
SecureStorageInterface secureStorageInterface,
) async {
Map<String, dynamic> prefs =
revertToState.validJSON["prefs"] as Map<String, dynamic>;
List<dynamic>? addressBookEntries =
@ -788,7 +838,9 @@ abstract class SWB {
}
// nodes
NodeService nodeService = NodeService();
NodeService nodeService = NodeService(
secureStorageInterface: secureStorageInterface,
);
final currentNodes = nodeService.nodes;
if (nodes == null) {
// no pre nodes found so we delete all but defaults
@ -914,7 +966,8 @@ abstract class SWB {
}
// finally remove any added wallets
final walletsService = WalletsService();
final walletsService =
WalletsService(secureStorageInterface: secureStorageInterface);
final namesData = await walletsService.walletNames;
for (final entry in namesData.entries) {
if (!revertToState.walletIds.contains(entry.value.walletId)) {
@ -989,8 +1042,11 @@ abstract class SWB {
static Future<void> _restoreNodes(
List<dynamic>? nodes,
List<dynamic>? primaryNodes,
SecureStorageInterface secureStorageInterface,
) async {
NodeService nodeService = NodeService();
NodeService nodeService = NodeService(
secureStorageInterface: secureStorageInterface,
);
if (nodes != null) {
for (var node in nodes) {
await nodeService.add(

View file

@ -4,15 +4,16 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stackwallet/utilities/util.dart';
class StackFileSystem {
class SWBFileSystem {
Directory? rootPath;
Directory? startPath;
String? filePath;
String? dirPath;
final bool isDesktop = !(Platform.isAndroid || Platform.isIOS);
final bool isDesktop = Util.isDesktop;
Future<Directory> prepareStorage() async {
if (Platform.isAndroid) {
@ -25,11 +26,20 @@ class StackFileSystem {
}
debugPrint(rootPath!.absolute.toString());
Directory sampleFolder =
Directory('${rootPath!.path}Documents/Stack_backups');
late Directory sampleFolder;
if (Platform.isIOS) {
sampleFolder = Directory(rootPath!.path);
} else if (Platform.isAndroid) {
sampleFolder = Directory('${rootPath!.path}Documents/Stack_backups');
} else if (Platform.isLinux) {
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
} else if (Platform.isWindows) {
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
} else if (Platform.isMacOS) {
sampleFolder = Directory('${rootPath!.path}/Stack_backups');
}
try {
if (!sampleFolder.existsSync()) {
sampleFolder.createSync(recursive: true);

View file

@ -7,9 +7,8 @@ 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/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/stack_file_system.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/home/settings_menu/backup_and_restore/restore_backup_dialog.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -44,24 +43,13 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
late final FocusNode passwordFocusNode;
late final StackFileSystem stackFileSystem;
late final SWBFileSystem stackFileSystem;
bool hidePassword = true;
Future<void> restoreBackupPopup(BuildContext context) async {
// await showDialog<dynamic>(
// context: context,
// useSafeArea: false,
// barrierDismissible: true,
// builder: (context) {
// return const RestoreBackupDialog();
// },
// );
}
@override
void initState() {
stackFileSystem = StackFileSystem();
stackFileSystem = SWBFileSystem();
fileLocationController = TextEditingController();
passwordController = TextEditingController();
@ -237,7 +225,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
enableSuggestions: false,
autocorrect: false,
decoration: standardInputDecoration(
"Enter password",
"Enter passphrase",
passwordFocusNode,
context,
).copyWith(
@ -534,7 +522,7 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
const EdgeInsets
.all(32),
child: Text(
"Restoring Stack Wallet",
"Restore Stack Wallet",
style: STextStyles
.desktopH3(
context),
@ -546,12 +534,10 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
const DesktopDialogCloseButton(),
],
),
const SizedBox(
height: 30,
),
Padding(
padding: EdgeInsets
.symmetric(
padding:
const EdgeInsets
.symmetric(
horizontal:
32),
child:
@ -560,6 +546,9 @@ class _RestoreFromFileViewState extends ConsumerState<RestoreFromFileView> {
jsonString,
),
),
const SizedBox(
height: 32,
),
],
),
),

View file

@ -9,6 +9,7 @@ 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/providers/global/secure_store_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/stack_restore/stack_restoring_ui_state_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -19,10 +20,13 @@ 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/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 '../../../../../widgets/desktop/primary_button.dart';
class StackRestoreProgressView extends ConsumerStatefulWidget {
const StackRestoreProgressView({
Key? key,
@ -100,6 +104,30 @@ class _StackRestoreProgressViewState
context: context,
builder: (_) => const CancelStackRestoreDialog(),
);
// : await Row(
// children: [
// SecondaryButton(
// width: 248,
// desktopMed: true,
// enabled: true,
// label: "Keep restoring",
// onPressed: () {
// false;
// },
// ),
// const SizedBox(width: 16),
// PrimaryButton(
// width: 248,
// desktopMed: true,
// enabled: true,
// label: "Cancel anyway",
// onPressed: () {
// true;
// },
// )
// ],
// );
if (result is bool && result) {
return true;
}
@ -115,6 +143,7 @@ class _StackRestoreProgressViewState
finished = await SWB.restoreStackWalletJSON(
widget.jsonString,
uiState,
ref.read(secureStoreProvider),
);
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
@ -128,6 +157,7 @@ class _StackRestoreProgressViewState
}
bool _success = false;
bool pendingCancel = false;
Future<bool> _onWillPop() async {
if (_success) {
@ -239,7 +269,7 @@ class _StackRestoreProgressViewState
left: 4,
top: 4,
right: 4,
bottom: 0,
bottom: 4,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -255,40 +285,84 @@ class _StackRestoreProgressViewState
builder: (_, ref, __) {
final state = ref.watch(stackRestoringUIStateProvider
.select((value) => value.preferences));
return RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.gear,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
return !isDesktop
? RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.gear,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Preferences",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
);
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Preferences",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
)
: RoundedContainer(
padding: EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.popupBG,
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.gear,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Preferences",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
),
);
},
),
const SizedBox(
@ -298,39 +372,82 @@ class _StackRestoreProgressViewState
builder: (_, ref, __) {
final state = ref.watch(stackRestoringUIStateProvider
.select((value) => value.addressBook));
return RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: AddressBookIcon(
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
return !isDesktop
? RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: AddressBookIcon(
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Address book",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
);
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Address book",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
)
: RoundedContainer(
padding: EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.popupBG,
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: AddressBookIcon(
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Address book",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
),
);
},
),
const SizedBox(
@ -340,40 +457,83 @@ class _StackRestoreProgressViewState
builder: (_, ref, __) {
final state = ref.watch(stackRestoringUIStateProvider
.select((value) => value.nodes));
return RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.node,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
return !isDesktop
? RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.node,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Nodes",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
);
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Nodes",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
)
: RoundedContainer(
padding: EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.popupBG,
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.node,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Nodes",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
));
},
),
const SizedBox(
@ -383,40 +543,86 @@ class _StackRestoreProgressViewState
builder: (_, ref, __) {
final state = ref.watch(stackRestoringUIStateProvider
.select((value) => value.trades));
return RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.arrowRotate2,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
return !isDesktop
? Container(
child: RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.arrowRotate2,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Exchange history",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Exchange history",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
);
)
: RoundedContainer(
padding: EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.popupBG,
borderColor: Theme.of(context)
.extension<StackColors>()!
.background,
child: RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
child: Center(
child: SvgPicture.asset(
Assets.svg.arrowRotate2,
width: 16,
height: 16,
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
),
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(state),
),
title: "Exchange history",
subTitle: state == StackRestoringStatus.failed
? Text(
"Something went wrong",
style: STextStyles.errorSmall(context),
)
: null,
),
);
},
),
const SizedBox(
@ -446,28 +652,55 @@ class _StackRestoreProgressViewState
),
SizedBox(
width: MediaQuery.of(context).size.width - 32,
child: TextButton(
onPressed: () async {
if (_success) {
Navigator.of(context).pop();
} else {
if (await _requestCancel()) {
await _cancel();
}
}
},
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
child: Text(
_success ? "OK" : "Cancel restore process",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
),
),
child: !isDesktop
? TextButton(
onPressed: () async {
if (_success) {
Navigator.of(context).pop();
} else {
if (await _requestCancel()) {
await _cancel();
}
}
},
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
child: Text(
_success ? "OK" : "Cancel restore process",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_success
? PrimaryButton(
width: 248,
desktopMed: true,
enabled: true,
label: "Done",
onPressed: () async {
Navigator.of(context).pop();
},
)
: SecondaryButton(
width: 248,
desktopMed: true,
enabled: true,
label: "Cancel restore process",
onPressed: () async {
if (await _requestCancel()) {
await _cancel();
}
},
),
],
),
),
],
),

View file

@ -11,6 +11,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/stack_restoring_status.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/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
@ -68,140 +69,287 @@ class _RestoringWalletCardState extends ConsumerState<RestoringWalletCard> {
final coin = ref.watch(provider.select((value) => value.coin));
final restoringStatus =
ref.watch(provider.select((value) => value.restoringState));
return RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context).extension<StackColors>()!.colorForCoin(coin),
child: Center(
child: SvgPicture.asset(
Assets.svg.iconFor(
coin: coin,
),
height: 20,
width: 20,
),
),
),
),
onRightTapped: restoringStatus == StackRestoringStatus.failed
? () async {
final manager = ref.read(provider).manager!;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.restoring);
try {
final mnemonicList = await manager.mnemonic;
int maxUnusedAddressGap = 20;
if (coin == Coin.firo) {
maxUnusedAddressGap = 50;
}
const maxNumberOfIndexesToCheck = 1000;
if (mnemonicList.isEmpty) {
await manager.recoverFromMnemonic(
mnemonic: ref.read(provider).mnemonic!,
maxUnusedAddressGap: maxUnusedAddressGap,
maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck,
height: ref.read(provider).height ?? 0,
);
} else {
await manager.fullRescan(
maxUnusedAddressGap,
maxNumberOfIndexesToCheck,
);
}
if (mounted) {
final address = await manager.currentReceivingAddress;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.success,
address: address,
);
}
} catch (_) {
if (mounted) {
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.failed,
);
}
}
}
: null,
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(
ref.watch(provider.select((value) => value.restoringState)),
),
),
title:
"${ref.watch(provider.select((value) => value.walletName))} (${coin.ticker})",
subTitle: restoringStatus == StackRestoringStatus.failed
? Text(
"Unable to restore. Tap icon to retry.",
style: STextStyles.errorSmall(context),
)
: ref.watch(provider.select((value) => value.address)) != null
? Text(
ref.watch(provider.select((value) => value.address))!,
style: STextStyles.infoSmall(context),
)
: null,
button: restoringStatus == StackRestoringStatus.failed
? Container(
height: 20,
decoration: BoxDecoration(
return !Util.isDesktop
? RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
borderRadius: BorderRadius.circular(
1000,
),
),
child: RawMaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
.colorForCoin(coin),
child: Center(
child: SvgPicture.asset(
Assets.svg.iconFor(
coin: coin,
),
height: 20,
width: 20,
),
),
onPressed: () async {
final mnemonic = ref.read(provider).mnemonic;
),
),
onRightTapped: restoringStatus == StackRestoringStatus.failed
? () async {
final manager = ref.read(provider).manager!;
if (mnemonic != null) {
Navigator.of(context).push(
RouteGenerator.getRoute(
builder: (_) => RecoverPhraseView(
walletName: ref.read(provider).walletName,
mnemonic: mnemonic.split(" "),
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.restoring);
try {
final mnemonicList = await manager.mnemonic;
int maxUnusedAddressGap = 20;
if (coin == Coin.firo) {
maxUnusedAddressGap = 50;
}
const maxNumberOfIndexesToCheck = 1000;
if (mnemonicList.isEmpty) {
await manager.recoverFromMnemonic(
mnemonic: ref.read(provider).mnemonic!,
maxUnusedAddressGap: maxUnusedAddressGap,
maxNumberOfIndexesToCheck: maxNumberOfIndexesToCheck,
height: ref.read(provider).height ?? 0,
);
} else {
await manager.fullRescan(
maxUnusedAddressGap,
maxNumberOfIndexesToCheck,
);
}
if (mounted) {
final address = await manager.currentReceivingAddress;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.success,
address: address,
);
}
} catch (_) {
if (mounted) {
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.failed,
);
}
}
}
: null,
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(
ref.watch(provider.select((value) => value.restoringState)),
),
),
title:
"${ref.watch(provider.select((value) => value.walletName))} (${coin.ticker})",
subTitle: restoringStatus == StackRestoringStatus.failed
? Text(
"Unable to restore. Tap icon to retry.",
style: STextStyles.errorSmall(context),
)
: ref.watch(provider.select((value) => value.address)) != null
? Text(
ref.watch(provider.select((value) => value.address))!,
style: STextStyles.infoSmall(context),
)
: null,
button: restoringStatus == StackRestoringStatus.failed
? Container(
height: 20,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
borderRadius: BorderRadius.circular(
1000,
),
),
child: RawMaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
),
),
);
}
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"Show recovery phrase",
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
onPressed: () async {
final mnemonic = ref.read(provider).mnemonic;
if (mnemonic != null) {
Navigator.of(context).push(
RouteGenerator.getRoute(
builder: (_) => RecoverPhraseView(
walletName: ref.read(provider).walletName,
mnemonic: mnemonic.split(" "),
),
),
);
}
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"Show recovery phrase",
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
),
)
: null,
)
: RoundedContainer(
padding: EdgeInsets.all(0),
color: Theme.of(context).extension<StackColors>()!.popupBG,
borderColor: Theme.of(context).extension<StackColors>()!.background,
child: RestoringItemCard(
left: SizedBox(
width: 32,
height: 32,
child: RoundedContainer(
padding: const EdgeInsets.all(0),
color: Theme.of(context)
.extension<StackColors>()!
.colorForCoin(coin),
child: Center(
child: SvgPicture.asset(
Assets.svg.iconFor(
coin: coin,
),
height: 20,
width: 20,
),
),
),
),
)
: null,
);
onRightTapped: restoringStatus == StackRestoringStatus.failed
? () async {
final manager = ref.read(provider).manager!;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.restoring);
try {
final mnemonicList = await manager.mnemonic;
int maxUnusedAddressGap = 20;
if (coin == Coin.firo) {
maxUnusedAddressGap = 50;
}
const maxNumberOfIndexesToCheck = 1000;
if (mnemonicList.isEmpty) {
await manager.recoverFromMnemonic(
mnemonic: ref.read(provider).mnemonic!,
maxUnusedAddressGap: maxUnusedAddressGap,
maxNumberOfIndexesToCheck:
maxNumberOfIndexesToCheck,
height: ref.read(provider).height ?? 0,
);
} else {
await manager.fullRescan(
maxUnusedAddressGap,
maxNumberOfIndexesToCheck,
);
}
if (mounted) {
final address = await manager.currentReceivingAddress;
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.success,
address: address,
);
}
} catch (_) {
if (mounted) {
ref.read(stackRestoringUIStateProvider).update(
walletId: manager.walletId,
restoringStatus: StackRestoringStatus.failed,
);
}
}
}
: null,
right: SizedBox(
width: 20,
height: 20,
child: _getIconForState(
ref.watch(provider.select((value) => value.restoringState)),
),
),
title:
"${ref.watch(provider.select((value) => value.walletName))} (${coin.ticker})",
subTitle: restoringStatus == StackRestoringStatus.failed
? Text(
"Unable to restore. Tap icon to retry.",
style: STextStyles.errorSmall(context),
)
: ref.watch(provider.select((value) => value.address)) != null
? Text(
ref.watch(provider.select((value) => value.address))!,
style: STextStyles.infoSmall(context),
)
: null,
button: restoringStatus == StackRestoringStatus.failed
? Container(
height: 20,
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
borderRadius: BorderRadius.circular(
1000,
),
),
child: RawMaterialButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
splashColor: Theme.of(context)
.extension<StackColors>()!
.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
1000,
),
),
onPressed: () async {
final mnemonic = ref.read(provider).mnemonic;
if (mnemonic != null) {
Navigator.of(context).push(
RouteGenerator.getRoute(
builder: (_) => RecoverPhraseView(
walletName: ref.read(provider).walletName,
mnemonic: mnemonic.split(" "),
),
),
);
}
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
"Show recovery phrase",
style: STextStyles.infoSmall(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
),
),
)
: null,
),
);
}
}

View file

@ -7,10 +7,14 @@ import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/pages/wallet_view/sub_widgets/no_transactions_found.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import 'package:stackwallet/widgets/transaction_card.dart';
@ -94,18 +98,79 @@ class _TransactionsListState extends ConsumerState<TransactionsList> {
// this may mess with combined firo transactions
key: Key(tx.toString() + trade.uuid), //
trade: trade,
onTap: () {
unawaited(
Navigator.of(context).pushNamed(
TradeDetailsView.routeName,
arguments: Tuple4(
trade.tradeId,
tx,
widget.walletId,
ref.read(managerProvider).walletName,
onTap: () async {
if (Util.isDesktop) {
await showDialog<void>(
context: context,
builder: (context) => Navigator(
initialRoute: TradeDetailsView.routeName,
onGenerateRoute: RouteGenerator.generateRoute,
onGenerateInitialRoutes: (_, __) {
return [
FadePageRoute(
DesktopDialog(
// maxHeight:
// MediaQuery.of(context).size.height - 64,
maxHeight: double.infinity,
maxWidth: 580,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
bottom: 16,
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Trade details",
style: STextStyles.desktopH3(context),
),
DesktopDialogCloseButton(
onPressedOverride: Navigator.of(
context,
rootNavigator: true,
).pop,
),
],
),
),
Flexible(
child: TradeDetailsView(
tradeId: trade.tradeId,
transactionIfSentFromStack: tx,
walletName:
ref.read(managerProvider).walletName,
walletId: widget.walletId,
),
),
],
),
),
const RouteSettings(
name: TradeDetailsView.routeName,
),
),
];
},
),
),
);
);
} else {
unawaited(
Navigator.of(context).pushNamed(
TradeDetailsView.routeName,
arguments: Tuple4(
trade.tradeId,
tx,
widget.walletId,
ref.read(managerProvider).walletName,
),
),
);
}
},
)
],

View file

@ -2,11 +2,11 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
@ -23,15 +23,10 @@ import 'package:zxcvbn/zxcvbn.dart';
class CreatePasswordView extends ConsumerStatefulWidget {
const CreatePasswordView({
Key? key,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
}) : super(key: key);
static const String routeName = "/createPasswordDesktop";
final FlutterSecureStorageInterface secureStore;
@override
ConsumerState<CreatePasswordView> createState() => _CreatePasswordViewState();
}
@ -79,7 +74,13 @@ class _CreatePasswordViewState extends ConsumerState<CreatePasswordView> {
}
try {
if (await ref.read(storageCryptoHandlerProvider).hasPassword()) {
throw Exception(
"Tried creating a new password and attempted to overwrite an existing entry!");
}
await ref.read(storageCryptoHandlerProvider).initFromNew(passphrase);
await (ref.read(secureStoreProvider).store as DesktopSecureStore).init();
} catch (e) {
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,

View file

@ -1,9 +1,17 @@
import 'dart:async';
import 'package:flutter/material.dart';
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/forgot_password_desktop_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.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/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
@ -11,21 +19,23 @@ import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
class DesktopLoginView extends StatefulWidget {
class DesktopLoginView extends ConsumerStatefulWidget {
const DesktopLoginView({
Key? key,
this.startupWalletId,
this.load,
}) : super(key: key);
static const String routeName = "/desktopLogin";
final String? startupWalletId;
final Future<void> Function()? load;
@override
State<DesktopLoginView> createState() => _DesktopLoginViewState();
ConsumerState<DesktopLoginView> createState() => _DesktopLoginViewState();
}
class _DesktopLoginViewState extends State<DesktopLoginView> {
class _DesktopLoginViewState extends ConsumerState<DesktopLoginView> {
late final TextEditingController passwordController;
late final FocusNode passwordFocusNode;
@ -33,6 +43,34 @@ class _DesktopLoginViewState extends State<DesktopLoginView> {
bool hidePassword = true;
bool _continueEnabled = false;
Future<void> login() async {
try {
await ref
.read(storageCryptoHandlerProvider)
.initFromExisting(passwordController.text);
await (ref.read(secureStoreProvider).store as DesktopSecureStore).init();
await widget.load?.call();
// if no errors passphrase is correct
if (mounted) {
unawaited(
Navigator.of(context).pushNamedAndRemoveUntil(
DesktopHomeView.routeName,
(route) => false,
),
);
}
} catch (e) {
await showFloatingFlushBar(
type: FlushBarType.warning,
message: e.toString(),
context: context,
);
}
}
@override
void initState() {
passwordController = TextEditingController();
@ -153,14 +191,7 @@ class _DesktopLoginViewState extends State<DesktopLoginView> {
PrimaryButton(
label: "Continue",
enabled: _continueEnabled,
onPressed: () {
// todo auth
Navigator.of(context).pushNamedAndRemoveUntil(
DesktopHomeView.routeName,
(route) => false,
);
},
onPressed: login,
),
const SizedBox(
height: 60,

View file

@ -1,12 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/contact.dart';
import 'package:stackwallet/pages/address_book_views/subviews/add_address_book_entry_view.dart';
import 'package:stackwallet/pages/address_book_views/subviews/address_book_filter_view.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart';
@ -24,8 +31,43 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
late final FocusNode _searchFocusNode;
List<Contact>? _cache;
List<Contact>? _cacheFav;
late bool hasContacts = false;
String filter = "";
Future<void> selectCryptocurrency() async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return const DesktopDialog(
maxHeight: 609,
maxWidth: 576,
child: AddressBookFilterView(),
);
},
);
}
Future<void> newContact() async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return const DesktopDialog(
maxHeight: 609,
maxWidth: 576,
child: AddAddressBookEntryView(),
);
},
);
}
@override
void initState() {
_searchController = TextEditingController();
@ -46,9 +88,11 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
final hasWallets = ref.watch(walletsChangeNotifierProvider).hasWallets;
final size = MediaQuery.of(context).size;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DesktopAppBar(
isCompactHeight: true,
@ -67,72 +111,148 @@ class _DesktopAddressBook extends ConsumerState<DesktopAddressBook> {
const SizedBox(height: 53),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
children: [
SizedBox(
height: 60,
width: 489,
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: false,
enableSuggestions: false,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: (newString) {
setState(() => filter = newString);
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search...",
_searchFocusNode,
context,
).copyWith(
labelStyle: STextStyles.fieldLabel(context)
.copyWith(fontSize: 16),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
child: RoundedContainer(
color: Theme.of(context).extension<StackColors>()!.background,
child: Row(
children: [
SizedBox(
height: 60,
width: size.width - 800,
child: ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
autocorrect: false,
enableSuggestions: false,
controller: _searchController,
focusNode: _searchFocusNode,
onChanged: (newString) {
setState(() => filter = newString);
},
style: STextStyles.field(context),
decoration: standardInputDecoration(
"Search...",
_searchFocusNode,
context,
).copyWith(
labelStyle: STextStyles.fieldLabel(context)
.copyWith(fontSize: 16),
prefixIcon: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 16,
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
),
child: SvgPicture.asset(
Assets.svg.search,
width: 16,
height: 16,
),
),
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
filter = "";
});
},
),
],
suffixIcon: _searchController.text.isNotEmpty
? Padding(
padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox(
child: Row(
children: [
TextFieldIconButton(
child: const XIcon(),
onTap: () async {
setState(() {
_searchController.text = "";
filter = "";
});
},
),
],
),
),
),
)
: null,
)
: null,
),
),
),
),
),
],
const SizedBox(width: 20),
TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getDesktopMenuButtonColorSelected(context),
onPressed: () {
selectCryptocurrency();
},
child: SizedBox(
width: 200,
height: 56,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: SvgPicture.asset(Assets.svg.filter),
),
Text(
"Filter",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
),
],
),
),
),
const SizedBox(width: 20),
TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
newContact();
},
child: SizedBox(
width: 200,
height: 56,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: SvgPicture.asset(Assets.svg.circlePlus),
),
Text(
"Add new",
style: STextStyles.desktopTextExtraExtraSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.popupBG,
),
),
],
),
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 26),
child: SizedBox(
width: 489,
child: RoundedWhiteContainer(
child: Center(
child: Text(
"Your contacts will appear here",
style: STextStyles.itemSubtitle(context),
),
),
),
),
),
// Expanded(
// child: hasWallets ? const MyWallets() : const EmptyWallets(),
// ),
],
);
}

View file

@ -4,8 +4,12 @@ import 'package:stackwallet/pages_desktop_specific/home/address_book_view/deskto
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_settings_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_about_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_support_view.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/providers/global/notifications_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
@ -19,45 +23,73 @@ class DesktopHomeView extends ConsumerStatefulWidget {
}
class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
int currentViewIndex = 0;
final List<Widget> contentViews = [
const Navigator(
final Map<DesktopMenuItemId, Widget> contentViews = {
DesktopMenuItemId.myStack: const Navigator(
key: Key("desktopStackHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: MyStackView.routeName,
),
Container(
color: Colors.green,
// Container(
// // todo: exchange
// color: Colors.green,
// ),
DesktopMenuItemId.notifications: const Navigator(
key: Key("desktopNotificationsHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: DesktopNotificationsView.routeName,
),
Container(
color: Colors.red,
),
const Navigator(
DesktopMenuItemId.addressBook: const Navigator(
key: Key("desktopAddressBookHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: DesktopAddressBook.routeName,
),
const Navigator(
DesktopMenuItemId.settings: const Navigator(
key: Key("desktopSettingHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: DesktopSettingsView.routeName,
),
const Navigator(
DesktopMenuItemId.support: const Navigator(
key: Key("desktopSupportHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: DesktopSupportView.routeName,
),
const Navigator(
DesktopMenuItemId.about: const Navigator(
key: Key("desktopAboutHomeKey"),
onGenerateRoute: RouteGenerator.generateRoute,
initialRoute: DesktopAboutView.routeName,
),
];
};
void onMenuSelectionChanged(int newIndex) {
setState(() {
currentViewIndex = newIndex;
});
void onMenuSelectionWillChange(DesktopMenuItemId newKey) {
// check for unread notifications and refresh provider before
// showing notifications view
if (newKey == DesktopMenuItemId.notifications) {
ref.refresh(unreadNotificationsStateProvider);
}
// mark notifications as read if leaving notifications view
if (ref.read(currentDesktopMenuItemProvider.state).state ==
DesktopMenuItemId.notifications &&
newKey != DesktopMenuItemId.notifications) {
final Set<int> unreadNotificationIds =
ref.read(unreadNotificationsStateProvider.state).state;
if (unreadNotificationIds.isNotEmpty) {
List<Future<void>> futures = [];
for (int i = 0; i < unreadNotificationIds.length - 1; i++) {
futures.add(ref
.read(notificationsProvider)
.markAsRead(unreadNotificationIds.elementAt(i), false));
}
// wait for multiple to update if any
Future.wait(futures).then((_) {
// only notify listeners once
ref
.read(notificationsProvider)
.markAsRead(unreadNotificationIds.last, true);
});
}
}
}
@override
@ -67,14 +99,16 @@ class _DesktopHomeViewState extends ConsumerState<DesktopHomeView> {
child: Row(
children: [
DesktopMenu(
onSelectionChanged: onMenuSelectionChanged,
// onSelectionChanged: onMenuSelectionChanged,
onSelectionWillChange: onMenuSelectionWillChange,
),
Container(
width: 1,
color: Theme.of(context).extension<StackColors>()!.background,
),
Expanded(
child: contentViews[currentViewIndex],
child: contentViews[
ref.watch(currentDesktopMenuItemProvider.state).state]!,
),
],
),

View file

@ -4,17 +4,30 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu_item.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
enum DesktopMenuItemId {
myStack,
exchange,
notifications,
addressBook,
settings,
support,
about,
}
class DesktopMenu extends ConsumerStatefulWidget {
const DesktopMenu({
Key? key,
required this.onSelectionChanged,
this.onSelectionChanged,
this.onSelectionWillChange,
}) : super(key: key);
final void Function(int)? onSelectionChanged;
final void Function(DesktopMenuItemId)? onSelectionChanged;
final void Function(DesktopMenuItemId)? onSelectionWillChange;
@override
ConsumerState<DesktopMenu> createState() => _DesktopMenuState();
@ -25,13 +38,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
static const minimizedWidth = 72.0;
double _width = expandedWidth;
int selectedMenuItem = 0;
void updateSelectedMenuItem(int index) {
setState(() {
selectedMenuItem = index;
});
widget.onSelectionChanged?.call(index);
void updateSelectedMenuItem(DesktopMenuItemId idKey) {
widget.onSelectionWillChange?.call(idKey);
ref.read(currentDesktopMenuItemProvider.state).state = idKey;
widget.onSelectionChanged?.call(idKey);
}
void toggleMinimize() {
@ -85,7 +98,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.walletDesktop,
width: 20,
height: 20,
color: 0 == selectedMenuItem
color: DesktopMenuItemId.myStack ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
@ -95,43 +111,47 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.withOpacity(0.8),
),
label: "My Stack",
value: 0,
group: selectedMenuItem,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
const SizedBox(
height: 2,
),
DesktopMenuItem(
icon: SvgPicture.asset(
Assets.svg.exchangeDesktop,
width: 20,
height: 20,
color: 1 == selectedMenuItem
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textDark
.withOpacity(0.8),
),
label: "Exchange",
value: 1,
group: selectedMenuItem,
value: DesktopMenuItemId.myStack,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
const SizedBox(
height: 2,
),
// DesktopMenuItem(
// icon: SvgPicture.asset(
// Assets.svg.exchangeDesktop,
// width: 20,
// height: 20,
// color: DesktopMenuItemId.exchange == ref.watch(currentDesktopMenuItemProvider.state).state
// ? Theme.of(context)
// .extension<StackColors>()!
// .textDark
// : Theme.of(context)
// .extension<StackColors>()!
// .textDark
// .withOpacity(0.8),
// ),
// label: "Exchange",
// value: DesktopMenuItemId.exchange,
// group: ref.watch(currentDesktopMenuItemProvider.state).state,
// onChanged: updateSelectedMenuItem,
// iconOnly: _width == minimizedWidth,
// ),
// const SizedBox(
// height: 2,
// ),
DesktopMenuItem(
icon: SvgPicture.asset(
Assets.svg.bell,
width: 20,
height: 20,
color: 2 == selectedMenuItem
color: DesktopMenuItemId.notifications ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
@ -141,8 +161,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.withOpacity(0.8),
),
label: "Notifications",
value: 2,
group: selectedMenuItem,
value: DesktopMenuItemId.notifications,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
@ -154,7 +175,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.addressBookDesktop,
width: 20,
height: 20,
color: 3 == selectedMenuItem
color: DesktopMenuItemId.addressBook ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
@ -164,8 +188,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.withOpacity(0.8),
),
label: "Address Book",
value: 3,
group: selectedMenuItem,
value: DesktopMenuItemId.addressBook,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
@ -177,7 +202,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.gear,
width: 20,
height: 20,
color: 4 == selectedMenuItem
color: DesktopMenuItemId.settings ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
@ -187,8 +215,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.withOpacity(0.8),
),
label: "Settings",
value: 4,
group: selectedMenuItem,
value: DesktopMenuItemId.settings,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
@ -200,7 +229,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.messageQuestion,
width: 20,
height: 20,
color: 5 == selectedMenuItem
color: DesktopMenuItemId.support ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
@ -210,8 +242,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.withOpacity(0.8),
),
label: "Support",
value: 5,
group: selectedMenuItem,
value: DesktopMenuItemId.support,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
@ -223,7 +256,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
Assets.svg.aboutDesktop,
width: 20,
height: 20,
color: 6 == selectedMenuItem
color: DesktopMenuItemId.about ==
ref
.watch(currentDesktopMenuItemProvider.state)
.state
? Theme.of(context)
.extension<StackColors>()!
.textDark
@ -233,8 +269,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
.withOpacity(0.8),
),
label: "About",
value: 6,
group: selectedMenuItem,
value: DesktopMenuItemId.about,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: updateSelectedMenuItem,
iconOnly: _width == minimizedWidth,
),
@ -251,7 +288,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
),
label: "Exit",
value: 7,
group: selectedMenuItem,
group:
ref.watch(currentDesktopMenuItemProvider.state).state,
onChanged: (_) {
// todo: save stuff/ notify before exit?
exit(0);

View file

@ -0,0 +1,173 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
class DesktopAuthSend extends ConsumerStatefulWidget {
const DesktopAuthSend({Key? key}) : super(key: key);
@override
ConsumerState<DesktopAuthSend> createState() => _DesktopAuthSendState();
}
class _DesktopAuthSendState extends ConsumerState<DesktopAuthSend> {
late final TextEditingController passwordController;
late final FocusNode passwordFocusNode;
bool hidePassword = true;
bool _confirmEnabled = false;
Future<bool> verifyPassphrase() async {
return await ref
.read(storageCryptoHandlerProvider)
.verifyPassphrase(passwordController.text);
}
@override
void initState() {
passwordController = TextEditingController();
passwordFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
passwordController.dispose();
passwordFocusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
Assets.svg.keys,
width: 100,
),
const SizedBox(
height: 56,
),
Text(
"Confirm transaction",
style: STextStyles.desktopH3(context),
),
const SizedBox(
height: 16,
),
Text(
"Enter your wallet password to send BTC",
style: STextStyles.desktopTextMedium(context).copyWith(
color: Theme.of(context).extension<StackColors>()!.textDark3,
),
),
const SizedBox(
height: 24,
),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key("desktopLoginPasswordFieldKey"),
focusNode: passwordFocusNode,
controller: passwordController,
style: STextStyles.desktopTextMedium(context).copyWith(
height: 2,
),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
decoration: standardInputDecoration(
"Enter password",
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: 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(() {
_confirmEnabled = passwordController.text.isNotEmpty;
});
},
),
),
const SizedBox(
height: 48,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Cancel",
desktopMed: true,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
width: 16,
),
Expanded(
child: PrimaryButton(
enabled: _confirmEnabled,
label: "Confirm",
desktopMed: true,
onPressed: () async {
// TODO show spinner while verifying passphrase
final passwordIsValid = await verifyPassphrase();
if (mounted) {
Navigator.of(context).pop(passwordIsValid);
}
},
),
),
],
)
],
);
}
}

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:decimal/decimal.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -9,8 +10,8 @@ import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/fee_rate_type_state_provider.dart';
@ -332,6 +333,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
child: ConfirmTransactionView(
transactionInfo: txData,
walletId: walletId,
routeOnSuccessName: DesktopHomeView.routeName,
),
),
),
@ -550,13 +552,13 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
Future<String?> _firoBalanceFuture(
ChangeNotifierProvider<Manager> provider,
String locale,
bool private,
) async {
final wallet = ref.read(provider).wallet as FiroWallet?;
if (wallet != null) {
Decimal? balance;
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
if (private) {
balance = await wallet.availablePrivateBalance();
} else {
balance = await wallet.availablePublicBalance();
@ -572,24 +574,21 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
Widget firoBalanceFutureBuilder(
BuildContext context,
AsyncSnapshot<String?> snapshot,
bool private,
) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Private") {
if (private) {
_privateBalanceString = snapshot.data!;
} else {
_publicBalanceString = snapshot.data!;
}
}
if (ref.read(publicPrivateBalanceStateProvider.state).state == "Private" &&
_privateBalanceString != null) {
if (private && _privateBalanceString != null) {
return Text(
"$_privateBalanceString ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
);
} else if (ref.read(publicPrivateBalanceStateProvider.state).state ==
"Public" &&
_publicBalanceString != null) {
} else if (!private && _publicBalanceString != null) {
return Text(
"$_publicBalanceString ${coin.ticker}",
style: STextStyles.itemSubtitle(context),
@ -889,71 +888,95 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
height: 10,
),
if (coin == Coin.firo)
Stack(
children: [
TextField(
autocorrect: Util.isDesktop ? false : true,
enableSuggestions: Util.isDesktop ? false : true,
readOnly: true,
textInputAction: TextInputAction.none,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12,
),
child: RawMaterialButton(
splashColor:
Theme.of(context).extension<StackColors>()!.highlight,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
showModalBottomSheet<dynamic>(
backgroundColor: Colors.transparent,
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
builder: (_) => FiroBalanceSelectionSheet(
walletId: walletId,
),
);
},
DropdownButtonHideUnderline(
child: DropdownButton2(
offset: const Offset(0, -10),
isExpanded: true,
dropdownElevation: 0,
value: ref.watch(publicPrivateBalanceStateProvider.state).state,
items: [
DropdownMenuItem(
value: "Private",
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
"${ref.watch(publicPrivateBalanceStateProvider.state).state} balance",
style: STextStyles.itemSubtitle12(context),
),
const SizedBox(
width: 10,
),
FutureBuilder(
future: _firoBalanceFuture(provider, locale),
builder: firoBalanceFutureBuilder,
),
],
Text(
"Private balance",
style: STextStyles.itemSubtitle12(context),
),
SvgPicture.asset(
Assets.svg.chevronDown,
width: 8,
height: 4,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle2,
const SizedBox(
width: 10,
),
FutureBuilder(
future: _firoBalanceFuture(provider, locale, true),
builder: (context, AsyncSnapshot<String?> snapshot) =>
firoBalanceFutureBuilder(
context,
snapshot,
true,
),
),
],
),
),
)
],
DropdownMenuItem(
value: "Public",
child: Row(
children: [
Text(
"Public balance",
style: STextStyles.itemSubtitle12(context),
),
const SizedBox(
width: 10,
),
FutureBuilder(
future: _firoBalanceFuture(provider, locale, false),
builder: (context, AsyncSnapshot<String?> snapshot) =>
firoBalanceFutureBuilder(
context,
snapshot,
false,
),
),
],
),
),
],
onChanged: (value) {
if (value is String) {
setState(() {
ref.watch(publicPrivateBalanceStateProvider.state).state =
value;
});
}
},
icon: SvgPicture.asset(
Assets.svg.chevronDown,
width: 12,
height: 6,
color: Theme.of(context).extension<StackColors>()!.textDark3,
),
buttonPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
buttonDecoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
dropdownDecoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
),
if (coin == Coin.firo)
const SizedBox(

View file

@ -1,10 +1,15 @@
import 'dart:async';
import 'package:flutter/material.dart';
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/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/providers/providers.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/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
@ -196,36 +201,32 @@ class _UnlockWalletKeysDesktopState
enabled: continueEnabled,
onPressed: continueEnabled
? () async {
// todo: check password
// Navigator.of(context).pop();
final words = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.mnemonic;
final verified = await ref
.read(storageCryptoHandlerProvider)
.verifyPassphrase(passwordController.text);
await Navigator.of(context).pushReplacementNamed(
WalletKeysDesktopPopup.routeName,
arguments: words,
);
//
// await showDialog<void>(
// context: context,
// barrierDismissible: false,
// builder: (context) => Navigator(
// initialRoute: WalletKeysDesktopPopup.routeName,
// onGenerateRoute: RouteGenerator.generateRoute,
// onGenerateInitialRoutes: (_, __) {
// return [
// RouteGenerator.generateRoute(
// RouteSettings(
// name: WalletKeysDesktopPopup.routeName,
// arguments: words,
// ),
// )
// ];
// },
// ),
// );
if (verified) {
final words = await ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.mnemonic;
if (mounted) {
await Navigator.of(context)
.pushReplacementNamed(
WalletKeysDesktopPopup.routeName,
arguments: words,
);
}
} else {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Invalid passphrase!",
context: context,
),
);
}
}
: null,
),

View file

@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/notifications/notification_card.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class DesktopNotificationsView extends ConsumerStatefulWidget {
const DesktopNotificationsView({Key? key}) : super(key: key);
static const String routeName = "/desktopNotifications";
@override
ConsumerState<DesktopNotificationsView> createState() =>
_DesktopNotificationsViewState();
}
class _DesktopNotificationsViewState
extends ConsumerState<DesktopNotificationsView> {
@override
Widget build(BuildContext context) {
final notifications =
ref.watch(notificationsProvider.select((value) => value.notifications));
return DesktopScaffold(
background: Theme.of(context).extension<StackColors>()!.background,
appBar: DesktopAppBar(
isCompactHeight: true,
leading: Padding(
padding: const EdgeInsets.only(left: 24),
child: Text(
"Notifications",
style: STextStyles.desktopH3(context),
),
),
),
body: notifications.isEmpty
? RoundedWhiteContainer(
child: Center(
child: Text(
"Notifications will appear here",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
),
)
: ListView.builder(
primary: false,
itemCount: notifications.length,
itemBuilder: (context, index) {
final notification = notifications[index];
if (notification.read == false) {
ref
.read(unreadNotificationsStateProvider.state)
.state
.add(notification.id);
}
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 5,
),
child: NotificationCard(
notification: notification,
),
);
},
),
);
}
}

View file

@ -4,9 +4,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:intl/intl.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/create_backup_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/edit_auto_backup_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/create_auto_backup.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/enable_backup_dialog.dart';
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
@ -14,16 +16,18 @@ import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.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';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_container.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../../providers/global/auto_swb_service_provider.dart';
import '../../../../widgets/custom_buttons/blue_text_button.dart';
class BackupRestoreSettings extends ConsumerStatefulWidget {
const BackupRestoreSettings({Key? key}) : super(key: key);
@ -97,53 +101,171 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return CreateAutoBackup();
return const CreateAutoBackup();
},
);
}
Future<void> editAutoBackup() async {
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) => DesktopDialog(
maxWidth: 580,
maxHeight: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
"Edit auto backup",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
const Padding(
padding: EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: EditAutoBackupView(),
),
],
),
),
);
}
Future<void> attemptDisable() async {
final result = await showDialog<bool?>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return StackDialog(
title: "Disable Auto Backup",
message:
"You are turning off Auto Backup. You can turn it back on at any time. Your previous Auto Backup file will not be deleted. Remember to backup your wallets manually so you don't lose important information.",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Back",
style: STextStyles.button(context).copyWith(
color:
Theme.of(context).extension<StackColors>()!.accentColorDark,
),
),
onPressed: () {
Navigator.of(context).pop();
},
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
child: Text(
"Disable",
style: STextStyles.button(context),
),
onPressed: () {
Navigator.of(context).pop();
setState(() {
ref.watch(prefsChangeNotifierProvider).isAutoBackupEnabled =
false;
});
},
),
);
return !Util.isDesktop
? StackDialog(
title: "Disable Auto Backup",
message:
"You are turning off Auto Backup. You can turn it back on at any time. Your previous Auto Backup file will not be deleted. Remember to backup your wallets manually so you don't lose important information.",
leftButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonColor(context),
child: Text(
"Back",
style: STextStyles.button(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
onPressed: () {
Navigator.of(context).pop();
},
),
rightButton: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
child: Text(
"Disable",
style: STextStyles.button(context),
),
onPressed: () {
Navigator.of(context).pop();
setState(() {
ref
.watch(prefsChangeNotifierProvider)
.isAutoBackupEnabled = false;
});
},
),
)
: DesktopDialog(
maxHeight: double.infinity,
maxWidth: 580,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(
left: 32,
),
child: Text(
"Disable Auto Backup",
style: STextStyles.desktopH3(context),
),
),
const DesktopDialogCloseButton(),
],
),
Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 600,
child: Text(
"You are turning off Auto Backup. You can turn it back on at any time. Your previous Auto Backup file will not be deleted. Remember to backup your wallets manually so you don't lose important information.",
style: STextStyles.desktopTextSmall(context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3,
),
),
),
const SizedBox(
height: 48,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SecondaryButton(
desktopMed: true,
label: "Cancel",
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(width: 16),
Expanded(
child: PrimaryButton(
desktopMed: true,
label: "Disable",
onPressed: () {
ref
.read(prefsChangeNotifierProvider)
.isAutoBackupEnabled = false;
Navigator.of(context).pop();
},
),
),
],
),
],
),
),
],
),
);
},
);
if (mounted) {
@ -310,40 +432,32 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Container(
RoundedContainer(
width: 403,
color: Theme.of(context)
.extension<StackColors>()!
.background,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
"Backed up ${prettySinceLastBackupString(ref.watch(prefsChangeNotifierProvider.select((value) => value.lastAutoBackup)))}",
style: STextStyles
.itemSubtitle(
context),
),
BlueTextButton(
text: "Back up now",
onTap: () {
ref
.read(
autoSWBServiceProvider)
.doBackup();
},
),
],
),
],
),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
"Backed up ${prettySinceLastBackupString(ref.watch(prefsChangeNotifierProvider.select((value) => value.lastAutoBackup)))}",
style:
STextStyles.itemSubtitle(
context),
),
BlueTextButton(
text: "Back up now",
onTap: () {
ref
.read(
autoSWBServiceProvider)
.doBackup();
},
),
],
),
),
const SizedBox(
@ -365,7 +479,7 @@ class _BackupRestoreSettings extends ConsumerState<BackupRestoreSettings> {
width: 190,
label: "Edit auto backup",
onPressed: () {
createAutoBackup();
editAutoBackup();
},
),
],

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@ -5,26 +6,25 @@ import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stack_wallet_backup/stack_wallet_backup.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/stack_file_system.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/swb_file_system.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/enums/log_level_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/format.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/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.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';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/progress_bar.dart';
@ -35,13 +35,8 @@ import 'package:zxcvbn/zxcvbn.dart';
class CreateAutoBackup extends ConsumerStatefulWidget {
const CreateAutoBackup({
Key? key,
this.secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
}) : super(key: key);
final FlutterSecureStorageInterface secureStore;
@override
ConsumerState<CreateAutoBackup> createState() => _CreateAutoBackup();
}
@ -51,9 +46,9 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
late final TextEditingController passphraseController;
late final TextEditingController passphraseRepeatController;
late final FlutterSecureStorageInterface secureStore;
late final SecureStorageInterface secureStore;
late final StackFileSystem stackFileSystem;
late final SWBFileSystem stackFileSystem;
late final FocusNode passphraseFocusNode;
late final FocusNode passphraseRepeatFocusNode;
final zxcvbn = Zxcvbn();
@ -85,8 +80,8 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
@override
void initState() {
secureStore = widget.secureStore;
stackFileSystem = StackFileSystem();
secureStore = ref.read(secureStoreProvider);
stackFileSystem = SWBFileSystem();
fileLocationController = TextEditingController();
passphraseController = TextEditingController();
@ -125,10 +120,9 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType ");
bool isEnabledAutoBackup = ref.watch(prefsChangeNotifierProvider
.select((value) => value.isAutoBackupEnabled));
// bool isEnabledAutoBackup = ref.watch(prefsChangeNotifierProvider
// .select((value) => value.isAutoBackupEnabled));
String? selectedItem = "Every 10 minutes";
final isDesktop = Util.isDesktop;
return DesktopDialog(
maxHeight: 680,
@ -146,25 +140,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: AppBarIconButton(
color: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
size: 40,
icon: SvgPicture.asset(
Assets.svg.x,
color: Theme.of(context).extension<StackColors>()!.textDark,
width: 22,
height: 22,
),
onPressed: () {
int count = 0;
Navigator.of(context).popUntil((_) => count++ >= 2);
},
),
),
const DesktopDialogCloseButton(),
],
),
const SizedBox(
@ -407,7 +383,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
),
),
const SizedBox(
height: 10,
height: 16,
),
ClipRRect(
borderRadius: BorderRadius.circular(
@ -493,7 +469,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
child: isDesktop
? DropdownButtonHideUnderline(
child: DropdownButton2(
offset: Offset(0, -10),
offset: const Offset(0, -10),
isExpanded: true,
dropdownElevation: 0,
value: _currentDropDownValue,
@ -576,12 +552,8 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
Expanded(
child: SecondaryButton(
label: "Cancel",
onPressed: () {
int count = 0;
!isEnabledAutoBackup
? Navigator.of(context).popUntil((_) => count++ >= 2)
: Navigator.of(context).pop();
},
desktopMed: true,
onPressed: Navigator.of(context).pop,
),
),
const SizedBox(
@ -589,6 +561,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
),
Expanded(
child: PrimaryButton(
desktopMed: true,
label: "Enable Auto Backup",
enabled: shouldEnableCreate,
onPressed: !shouldEnableCreate
@ -601,44 +574,89 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
passphraseRepeatController.text;
if (pathToSave.isEmpty) {
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory not chosen",
context: context,
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory not chosen",
context: context,
),
);
return;
}
if (!(await Directory(pathToSave).exists())) {
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory does not exist",
context: context,
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Directory does not exist",
context: context,
),
);
return;
}
if (passphrase.isEmpty) {
showFloatingFlushBar(
type: FlushBarType.warning,
message: "A passphrase is required",
context: context,
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "A passphrase is required",
context: context,
),
);
return;
}
if (passphrase != repeatPassphrase) {
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase does not match",
context: context,
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase does not match",
context: context,
),
);
return;
}
showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => const StackDialog(
title: "Encrypting initial backup",
message: "This shouldn't take long",
unawaited(
showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) {
if (Util.isDesktop) {
return DesktopDialog(
maxHeight: double.infinity,
maxWidth: 450,
child: Padding(
padding: const EdgeInsets.all(
32,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Encrypting initial backup",
style: STextStyles.desktopH3(
context),
),
const SizedBox(
height: 40,
),
Text(
"This shouldn't take long",
style: STextStyles
.desktopTextExtraExtraSmall(
context),
),
],
),
),
);
} else {
return const StackDialog(
title: "Encrypting initial backup",
message: "This shouldn't take long",
);
}
},
),
);
@ -659,10 +677,12 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
.log("$err\n$s", level: LogLevel.Error);
// pop encryption progress dialog
Navigator.of(context).pop();
showFloatingFlushBar(
type: FlushBarType.warning,
message: err,
context: context,
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: err,
context: context,
),
);
return;
} catch (e, s) {
@ -670,10 +690,12 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
.log("$e\n$s", level: LogLevel.Error);
// pop encryption progress dialog
Navigator.of(context).pop();
showFloatingFlushBar(
type: FlushBarType.warning,
message: "$e",
context: context,
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "$e",
context: context,
),
);
return;
}
@ -688,7 +710,9 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
final String fileToSave =
createAutoBackupFilename(pathToSave, now);
final backup = await SWB.createStackWalletJSON();
final backup = await SWB.createStackWalletJSON(
secureStorage: secureStore,
);
bool result = await SWB.encryptStackWalletWithADK(
fileToSave,
@ -702,9 +726,7 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
if (mounted) {
// pop encryption progress dialog
int count = 0;
Navigator.of(context)
.popUntil((_) => count++ >= 2);
Navigator.of(context).pop();
if (result) {
ref
@ -721,22 +743,76 @@ class _CreateAutoBackup extends ConsumerState<CreateAutoBackup> {
await showDialog<dynamic>(
context: context,
barrierDismissible: false,
builder: (_) => Platform.isAndroid
? StackOkDialog(
title:
"Stack Auto Backup enabled and saved to:",
message: fileToSave,
)
: const StackOkDialog(
title: "Stack Auto Backup enabled!"),
builder: (context) {
if (Platform.isAndroid) {
return StackOkDialog(
title:
"Stack Auto Backup enabled and saved to:",
message: fileToSave,
);
} else if (Util.isDesktop) {
return DesktopDialog(
maxHeight: double.infinity,
maxWidth: 500,
child: Padding(
padding: const EdgeInsets.only(
left: 32,
right: 32,
bottom: 32,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
"Stack Auto Backup enabled!",
style:
STextStyles.desktopH3(
context),
),
const DesktopDialogCloseButton(),
],
),
const SizedBox(
height: 40,
),
Row(
children: [
const Spacer(),
Expanded(
child: PrimaryButton(
label: "Ok",
desktopMed: true,
onPressed: () {
Navigator.of(context)
.pop();
},
),
),
],
)
],
),
),
);
} else {
return const StackOkDialog(
title: "Stack Auto Backup enabled!",
);
}
},
);
if (mounted) {
passphraseController.text = "";
passphraseRepeatController.text = "";
int count = 0;
Navigator.of(context)
.popUntil((_) => count++ >= 2);
Navigator.of(context).pop();
}
} else {
await showDialog<dynamic>(

View file

@ -18,7 +18,7 @@ class EnableBackupDialog extends StatelessWidget {
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return CreateAutoBackup();
return const CreateAutoBackup();
},
);
}
@ -59,6 +59,7 @@ class EnableBackupDialog extends StatelessWidget {
children: [
Expanded(
child: SecondaryButton(
desktopMed: true,
label: "Cancel",
onPressed: () {
Navigator.of(context).pop();
@ -70,8 +71,10 @@ class EnableBackupDialog extends StatelessWidget {
),
Expanded(
child: PrimaryButton(
desktopMed: true,
label: "Continue",
onPressed: () {
Navigator.of(context).pop();
createAutoBackup();
},
),

View file

@ -1,12 +1,20 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/enable_backup_dialog.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.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/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/progress_bar.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:zxcvbn/zxcvbn.dart';
class SecuritySettings extends ConsumerStatefulWidget {
const SecuritySettings({Key? key}) : super(key: key);
@ -18,21 +26,112 @@ class SecuritySettings extends ConsumerStatefulWidget {
}
class _SecuritySettings extends ConsumerState<SecuritySettings> {
Future<void> enableAutoBackup() async {
// wait for keyboard to disappear
FocusScope.of(context).unfocus();
await Future<void>.delayed(
const Duration(milliseconds: 100),
);
late bool changePassword = false;
await showDialog<dynamic>(
context: context,
useSafeArea: false,
barrierDismissible: true,
builder: (context) {
return EnableBackupDialog();
},
);
late final TextEditingController passwordCurrentController;
late final TextEditingController passwordController;
late final TextEditingController passwordRepeatController;
late final FocusNode passwordCurrentFocusNode;
late final FocusNode passwordFocusNode;
late final FocusNode passwordRepeatFocusNode;
final zxcvbn = Zxcvbn();
bool hidePassword = true;
bool shouldShowPasswordHint = true;
double passwordStrength = 0.0;
bool get shouldEnableSave {
return passwordCurrentController.text.isNotEmpty &&
passwordController.text.isNotEmpty &&
passwordRepeatController.text.isNotEmpty;
}
String passwordFeedback =
"Add another word or two. Uncommon words are better. Use a few words, avoid common phrases. No need for symbols, digits, or uppercase letters.";
Future<bool> attemptChangePW() async {
final String pw = passwordCurrentController.text;
final String pwNew = passwordController.text;
final String pwNewRepeat = passwordRepeatController.text;
final verified =
await ref.read(storageCryptoHandlerProvider).verifyPassphrase(pw);
if (verified) {
if (pwNew != pwNewRepeat) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "New passphrase does not match!",
context: context,
),
);
return false;
} else {
final success =
await ref.read(storageCryptoHandlerProvider).changePassphrase(
pw,
pwNew,
);
if (success) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "Passphrase successfully changed",
context: context,
),
);
return true;
} else {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Passphrase change failed",
context: context,
),
);
return false;
}
}
} else {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Current passphrase is not valid!",
context: context,
),
);
return false;
}
}
@override
void initState() {
passwordCurrentController = TextEditingController();
passwordController = TextEditingController();
passwordRepeatController = TextEditingController();
passwordCurrentFocusNode = FocusNode();
passwordFocusNode = FocusNode();
passwordRepeatFocusNode = FocusNode();
super.initState();
}
@override
void dispose() {
passwordCurrentController.dispose();
passwordController.dispose();
passwordRepeatController.dispose();
passwordCurrentFocusNode.dispose();
passwordFocusNode.dispose();
passwordRepeatFocusNode.dispose();
super.dispose();
}
@override
@ -40,104 +139,394 @@ class _SecuritySettings extends ConsumerState<SecuritySettings> {
debugPrint("BUILD: $runtimeType");
return Column(
children: [
Padding(
padding: const EdgeInsets.only(
right: 30,
),
child: RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset(
Assets.svg.circleLock,
width: 48,
height: 48,
),
Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: RichText(
textAlign: TextAlign.start,
text: TextSpan(
children: [
TextSpan(
text: "Change Password",
style: STextStyles.desktopTextSmall(context),
),
TextSpan(
text:
"\n\nProtect your Stack Wallet with a strong password. Stack Wallet does not store "
"your password, and is therefore NOT able to restore it. Keep your password safe and secure.",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
],
),
),
),
),
Column(
Row(
children: [
Expanded(
child: RoundedWhiteContainer(
radiusMultiplier: 2,
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Padding(
padding: EdgeInsets.all(
10,
),
child: NewPasswordButton(),
children: [
SvgPicture.asset(
Assets.svg.circleLock,
width: 48,
height: 48,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 16,
),
Text(
"Change Password",
style: STextStyles.desktopTextSmall(context),
),
const SizedBox(
height: 8,
),
Text(
"Protect your Stack Wallet with a strong password. Stack Wallet does not store "
"your password, and is therefore NOT able to restore it. Keep your password safe and secure.",
style:
STextStyles.desktopTextExtraExtraSmall(context),
),
const SizedBox(
height: 20,
),
changePassword
? SizedBox(
width: 512,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Current password",
style: STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key(
"desktopSecurityRestoreFromFilePasswordFieldKey"),
focusNode: passwordCurrentFocusNode,
controller: passwordCurrentController,
style: STextStyles.field(context),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
decoration: standardInputDecoration(
"Enter current password",
passwordCurrentFocusNode,
context,
).copyWith(
labelStyle:
STextStyles.fieldLabel(context),
suffixIcon: UnconstrainedBox(
child: Row(
children: [
const SizedBox(
width: 16,
),
GestureDetector(
key: const Key(
"desktopSecurityRestoreFromFilePasswordFieldShowPasswordButtonKey"),
onTap: () async {
setState(() {
hidePassword =
!hidePassword;
});
},
child: SvgPicture.asset(
hidePassword
? Assets.svg.eye
: Assets.svg.eyeSlash,
color: Theme.of(context)
.extension<
StackColors>()!
.textDark3,
width: 16,
height: 16,
),
),
const SizedBox(
width: 12,
),
],
),
),
),
onChanged: (newValue) {
setState(() {});
},
),
),
const SizedBox(height: 16),
Text(
"New password",
style: STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key(
"desktopSecurityCreateNewPasswordFieldKey1"),
focusNode: passwordFocusNode,
controller: passwordController,
style: STextStyles.field(context),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
decoration: standardInputDecoration(
"Enter new password",
passwordFocusNode,
context,
).copyWith(
labelStyle:
STextStyles.fieldLabel(context),
suffixIcon: UnconstrainedBox(
child: Row(
children: [
const SizedBox(
width: 16,
),
GestureDetector(
key: const Key(
"desktopSecurityCreateNewPasswordButtonKey1"),
onTap: () async {
setState(() {
hidePassword =
!hidePassword;
});
},
child: SvgPicture.asset(
hidePassword
? Assets.svg.eye
: Assets.svg.eyeSlash,
color: Theme.of(context)
.extension<
StackColors>()!
.textDark3,
width: 16,
height: 16,
),
),
const SizedBox(
width: 12,
),
],
),
),
),
onChanged: (newValue) {
if (newValue.isEmpty) {
setState(() {
passwordFeedback = "";
});
return;
}
final result =
zxcvbn.evaluate(newValue);
String suggestionsAndTips = "";
for (var sug in result
.feedback.suggestions!
.toSet()) {
suggestionsAndTips += "$sug\n";
}
suggestionsAndTips +=
result.feedback.warning!;
String feedback =
// "Password Strength: ${((result.score! / 4.0) * 100).toInt()}%\n"
suggestionsAndTips;
passwordStrength = result.score! / 4;
// hack fix to format back string returned from zxcvbn
if (feedback
.contains("phrasesNo need")) {
feedback = feedback.replaceFirst(
"phrasesNo need",
"phrases\nNo need");
}
if (feedback.endsWith("\n")) {
feedback = feedback.substring(
0, feedback.length - 2);
}
setState(() {
passwordFeedback = feedback;
});
},
),
),
if (passwordFocusNode.hasFocus ||
passwordRepeatFocusNode.hasFocus ||
passwordController.text.isNotEmpty)
Padding(
padding: EdgeInsets.only(
left: 12,
right: 12,
top: passwordFeedback.isNotEmpty
? 4
: 0,
),
child: passwordFeedback.isNotEmpty
? Text(
passwordFeedback,
style: STextStyles.infoSmall(
context),
)
: null,
),
if (passwordFocusNode.hasFocus ||
passwordRepeatFocusNode.hasFocus ||
passwordController.text.isNotEmpty)
Padding(
padding: const EdgeInsets.only(
left: 12,
right: 12,
top: 10,
),
child: ProgressBar(
key: const Key(
"desktopSecurityCreateStackBackUpProgressBar"),
width: 450,
height: 5,
fillColor: passwordStrength < 0.51
? Theme.of(context)
.extension<StackColors>()!
.accentColorRed
: passwordStrength < 1
? Theme.of(context)
.extension<StackColors>()!
.accentColorYellow
: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
percent: passwordStrength < 0.25
? 0.03
: passwordStrength,
),
),
const SizedBox(height: 16),
Text(
"Confirm new password",
style: STextStyles
.desktopTextExtraExtraSmall(
context)
.copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textDark3),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
child: TextField(
key: const Key(
"desktopSecurityCreateNewPasswordFieldKey2"),
focusNode: passwordRepeatFocusNode,
controller: passwordRepeatController,
style: STextStyles.field(context),
obscureText: hidePassword,
enableSuggestions: false,
autocorrect: false,
decoration: standardInputDecoration(
"Confirm new password",
passwordRepeatFocusNode,
context,
).copyWith(
labelStyle:
STextStyles.fieldLabel(context),
suffixIcon: UnconstrainedBox(
child: Row(
children: [
const SizedBox(
width: 16,
),
GestureDetector(
key: const Key(
"desktopSecurityCreateNewPasswordButtonKey2"),
onTap: () async {
setState(() {
hidePassword =
!hidePassword;
});
},
child: SvgPicture.asset(
hidePassword
? Assets.svg.eye
: Assets.svg.eyeSlash,
color: Theme.of(context)
.extension<
StackColors>()!
.textDark3,
width: 16,
height: 16,
),
),
const SizedBox(
width: 12,
),
],
),
),
),
onChanged: (newValue) {
setState(() {});
},
),
),
const SizedBox(height: 20),
PrimaryButton(
width: 160,
desktopMed: true,
enabled: shouldEnableSave,
label: "Save changes",
onPressed: () async {
final didChangePW =
await attemptChangePW();
if (didChangePW) {
setState(() {
changePassword = false;
});
}
},
)
],
),
)
: PrimaryButton(
width: 210,
desktopMed: true,
enabled: true,
label: "Set up new password",
onPressed: () {
setState(() {
changePassword = true;
});
},
),
],
),
],
),
],
),
),
),
const SizedBox(
width: 40,
),
],
),
],
);
}
}
class NewPasswordButton extends ConsumerWidget {
const NewPasswordButton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return SizedBox(
width: 200,
height: 48,
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getPrimaryEnabledButtonColor(context),
onPressed: () {
// Expandable(
// header: Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// NewPasswordButton(),
// ],
// ),
// body: Column(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// Text(
// "Current Password",
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
// color:
// Theme.of(context).extension<StackColors>()!.textDark3,
// ),
// textAlign: TextAlign.left,
// ),
// ],
// ),
// );
},
child: Text(
"Set up new password",
style: STextStyles.button(context),
),
),
);
}
}

View file

@ -0,0 +1,5 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_menu.dart';
final currentDesktopMenuItemProvider =
StateProvider<DesktopMenuItemId>((ref) => DesktopMenuItemId.myStack);

View file

@ -1,5 +1,9 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/services/auto_swb_service.dart';
final autoSWBServiceProvider =
ChangeNotifierProvider<AutoSWBService>((_) => AutoSWBService());
final autoSWBServiceProvider = ChangeNotifierProvider<AutoSWBService>(
(ref) => AutoSWBService(
secureStorageInterface: ref.read(secureStoreProvider),
),
);

View file

@ -1,15 +1,16 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/services/node_service.dart';
int _count = 0;
final nodeServiceChangeNotifierProvider =
ChangeNotifierProvider<NodeService>((_) {
ChangeNotifierProvider<NodeService>((ref) {
if (kDebugMode) {
_count++;
debugPrint(
"nodeServiceChangeNotifierProvider instantiation count: $_count");
}
return NodeService();
return NodeService(secureStorageInterface: ref.read(secureStoreProvider));
});

View file

@ -0,0 +1,18 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stackwallet/providers/desktop/storage_crypto_handler_provider.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/util.dart';
final secureStoreProvider = Provider<SecureStorageInterface>((ref) {
if (Util.isDesktop) {
final handler = ref.read(storageCryptoHandlerProvider).handler;
return SecureStorageWrapper(
store: DesktopSecureStore(handler), isDesktop: true);
} else {
return const SecureStorageWrapper(
store: FlutterSecureStorage(),
isDesktop: false,
);
}
});

View file

@ -1,16 +1,19 @@
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/global/secure_store_provider.dart';
import 'package:stackwallet/services/wallets_service.dart';
int _count = 0;
final walletsServiceChangeNotifierProvider =
ChangeNotifierProvider<WalletsService>((_) {
ChangeNotifierProvider<WalletsService>((ref) {
if (kDebugMode) {
_count++;
debugPrint(
"walletsServiceChangeNotifierProvider instantiation count: $_count");
}
return WalletsService();
return WalletsService(
secureStorageInterface: ref.read(secureStoreProvider),
);
});

View file

@ -93,6 +93,7 @@ import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/my_stack_v
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/qr_code_desktop_popup_content.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/wallet_keys_desktop_popup.dart';
import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_notifications_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/advanced_settings/advanced_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/appearance_settings.dart';
import 'package:stackwallet/pages_desktop_specific/home/settings_menu/backup_and_restore/backup_and_restore_settings.dart';
@ -1012,6 +1013,12 @@ class RouteGenerator {
builder: (_) => const DesktopHomeView(),
settings: RouteSettings(name: settings.name));
case DesktopNotificationsView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const DesktopNotificationsView(),
settings: RouteSettings(name: settings.name));
case DesktopSettingsView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,

View file

@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/helpers/restore_create_backup.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
@ -25,11 +24,9 @@ class AutoSWBService extends ChangeNotifier {
bool _isActiveTimer = false;
bool get isActivePeriodicTimer => _isActiveTimer;
final FlutterSecureStorageInterface secureStorageInterface;
final SecureStorageInterface secureStorageInterface;
AutoSWBService(
{this.secureStorageInterface =
const SecureStorageWrapper(FlutterSecureStorage())});
AutoSWBService({required this.secureStorageInterface});
/// Attempt a backup.
Future<void> doBackup() async {

View file

@ -12,7 +12,6 @@ import 'package:crypto/crypto.dart';
import 'package:decimal/decimal.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
@ -1357,7 +1356,7 @@ class BitcoinWallet extends CoinServiceAPI {
CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1369,7 +1368,7 @@ class BitcoinWallet extends CoinServiceAPI {
required CachedElectrumX cachedClient,
required TransactionNotificationTracker tracker,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore,
required SecureStorageInterface secureStore,
}) {
txTracker = tracker;
_walletId = walletId;
@ -1379,13 +1378,12 @@ class BitcoinWallet extends CoinServiceAPI {
_cachedElectrumXClient = cachedClient;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
@override
Future<void> updateNode(bool shouldRefresh) async {
final failovers = NodeService()
final failovers = NodeService(secureStorageInterface: _secureStore)
.failoverNodesFor(coin: coin)
.map((e) => ElectrumXNode(
address: e.host,
@ -1423,7 +1421,8 @@ class BitcoinWallet extends CoinServiceAPI {
}
Future<ElectrumXNode> getCurrentNode() async {
final node = NodeService().getPrimaryNodeFor(coin: coin) ??
final node = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
return ElectrumXNode(

View file

@ -13,7 +13,6 @@ import 'package:crypto/crypto.dart';
import 'package:decimal/decimal.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
@ -1262,7 +1261,7 @@ class BitcoinCashWallet extends CoinServiceAPI {
CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1274,7 +1273,7 @@ class BitcoinCashWallet extends CoinServiceAPI {
required CachedElectrumX cachedClient,
required TransactionNotificationTracker tracker,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore,
required SecureStorageInterface secureStore,
}) {
txTracker = tracker;
_walletId = walletId;
@ -1284,13 +1283,12 @@ class BitcoinCashWallet extends CoinServiceAPI {
_cachedElectrumXClient = cachedClient;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
@override
Future<void> updateNode(bool shouldRefresh) async {
final failovers = NodeService()
final failovers = NodeService(secureStorageInterface: _secureStore)
.failoverNodesFor(coin: coin)
.map((e) => ElectrumXNode(
address: e.host,
@ -1328,7 +1326,8 @@ class BitcoinCashWallet extends CoinServiceAPI {
}
Future<ElectrumXNode> getCurrentNode() async {
final node = NodeService().getPrimaryNodeFor(coin: coin) ??
final node = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
return ElectrumXNode(

View file

@ -13,6 +13,7 @@ import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
import 'package:stackwallet/services/coins/wownero/wownero_wallet.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'litecoin/litecoin_wallet.dart';
@ -24,6 +25,7 @@ abstract class CoinServiceAPI {
Coin coin,
String walletId,
String walletName,
SecureStorageInterface secureStorageInterface,
NodeModel node,
TransactionNotificationTracker tracker,
Prefs prefs,
@ -68,6 +70,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -77,6 +80,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -87,6 +91,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -97,6 +102,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -107,6 +113,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -117,6 +124,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -127,6 +135,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -137,6 +146,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -147,6 +157,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,
@ -157,6 +168,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
// tracker: tracker,
);
@ -165,6 +177,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
// tracker: tracker,
);
@ -173,6 +186,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
// tracker: tracker,
);
@ -181,6 +195,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
tracker: tracker,
cachedClient: cachedClient,
client: client,
@ -191,6 +206,7 @@ abstract class CoinServiceAPI {
walletId: walletId,
walletName: walletName,
coin: coin,
secureStore: secureStorageInterface,
client: client,
cachedClient: cachedClient,
tracker: tracker,

View file

@ -12,7 +12,6 @@ import 'package:crypto/crypto.dart';
import 'package:decimal/decimal.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
@ -1125,7 +1124,7 @@ class DogecoinWallet extends CoinServiceAPI {
CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1137,7 +1136,7 @@ class DogecoinWallet extends CoinServiceAPI {
required CachedElectrumX cachedClient,
required TransactionNotificationTracker tracker,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore,
required SecureStorageInterface secureStore,
}) {
txTracker = tracker;
_walletId = walletId;
@ -1147,13 +1146,12 @@ class DogecoinWallet extends CoinServiceAPI {
_cachedElectrumXClient = cachedClient;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
@override
Future<void> updateNode(bool shouldRefresh) async {
final failovers = NodeService()
final failovers = NodeService(secureStorageInterface: _secureStore)
.failoverNodesFor(coin: coin)
.map((e) => ElectrumXNode(
address: e.host,
@ -1191,7 +1189,8 @@ class DogecoinWallet extends CoinServiceAPI {
}
Future<ElectrumXNode> getCurrentNode() async {
final node = NodeService().getPrimaryNodeFor(coin: coin) ??
final node = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
return ElectrumXNode(

View file

@ -6,11 +6,9 @@ import 'dart:isolate';
import 'package:decimal/decimal.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_libepiccash/epic_cash.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart';
import 'package:mutex/mutex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:stack_wallet_backup/generate_password.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/node_model.dart';
@ -32,6 +30,7 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
import 'package:stackwallet/utilities/test_epic_box_connection.dart';
import 'package:tuple/tuple.dart';
@ -251,26 +250,29 @@ Future<String> _deleteWalletWrapper(String wallet) async {
Future<String> deleteEpicWallet({
required String walletId,
required FlutterSecureStorageInterface secureStore,
required SecureStorageInterface secureStore,
}) async {
String? config = await secureStore.read(key: '${walletId}_config');
if (Platform.isIOS) {
Directory appDir = (await getApplicationDocumentsDirectory());
if (Platform.isIOS) {
appDir = (await getLibraryDirectory());
}
if (Platform.isLinux) {
appDir = Directory("${appDir.path}/.stackwallet");
}
final path = "${appDir.path}/epiccash";
final String name = walletId;
final walletDir = '$path/$name';
var editConfig = jsonDecode(config as String);
editConfig["wallet_dir"] = walletDir;
config = jsonEncode(editConfig);
}
// is this even needed for anything?
// String? config = await secureStore.read(key: '${walletId}_config');
// // TODO: why double check for iOS?
// if (Platform.isIOS) {
// Directory appDir = await StackFileSystem.applicationRootDirectory();
// // todo why double check for ios?
// // if (Platform.isIOS) {
// // appDir = (await getLibraryDirectory());
// // }
// // if (Platform.isLinux) {
// // appDir = Directory("${appDir.path}/.stackwallet");
// // }
// final path = "${appDir.path}/epiccash";
// final String name = walletId;
//
// final walletDir = '$path/$name';
// var editConfig = jsonDecode(config as String);
//
// editConfig["wallet_dir"] = walletDir;
// config = jsonEncode(editConfig);
// }
final wallet = await secureStore.read(key: '${walletId}_wallet');
@ -518,14 +520,13 @@ class EpicCashWallet extends CoinServiceAPI {
required String walletName,
required Coin coin,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore}) {
required SecureStorageInterface secureStore}) {
_walletId = walletId;
_walletName = walletName;
_coin = coin;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
Logging.instance.log("$walletName isolate length: ${isolates.length}",
level: LogLevel.Info);
@ -537,7 +538,8 @@ class EpicCashWallet extends CoinServiceAPI {
@override
Future<void> updateNode(bool shouldRefresh) async {
_epicNode = NodeService().getPrimaryNodeFor(coin: coin) ??
_epicNode = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
// TODO notify ui/ fire event for node changed?
@ -659,7 +661,7 @@ class EpicCashWallet extends CoinServiceAPI {
@override
Coin get coin => _coin;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1238,13 +1240,8 @@ class EpicCashWallet extends CoinServiceAPI {
}
Future<String> currentWalletDirPath() async {
Directory appDir = (await getApplicationDocumentsDirectory());
if (Platform.isIOS) {
appDir = (await getLibraryDirectory());
}
if (Platform.isLinux) {
appDir = Directory("${appDir.path}/.stackwallet");
}
Directory appDir = await StackFileSystem.applicationRootDirectory();
final path = "${appDir.path}/epiccash";
final String name = _walletId.trim();
return '$path/$name';

View file

@ -11,7 +11,6 @@ import 'package:bitcoindart/bitcoindart.dart';
import 'package:decimal/decimal.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:lelantus/lelantus.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
@ -1306,7 +1305,7 @@ class FiroWallet extends CoinServiceAPI {
late CachedElectrumX _cachedElectrumXClient;
CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1321,7 +1320,7 @@ class FiroWallet extends CoinServiceAPI {
required CachedElectrumX cachedClient,
required TransactionNotificationTracker tracker,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore,
required SecureStorageInterface secureStore,
}) {
txTracker = tracker;
_walletId = walletId;
@ -1331,8 +1330,7 @@ class FiroWallet extends CoinServiceAPI {
_cachedElectrumXClient = cachedClient;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
Logging.instance.log("$walletName isolates length: ${isolates.length}",
level: LogLevel.Info);
@ -1870,7 +1868,7 @@ class FiroWallet extends CoinServiceAPI {
@override
Future<void> updateNode(bool shouldRefresh) async {
final failovers = NodeService()
final failovers = NodeService(secureStorageInterface: _secureStore)
.failoverNodesFor(coin: coin)
.map(
(e) => ElectrumXNode(
@ -3071,7 +3069,8 @@ class FiroWallet extends CoinServiceAPI {
}
Future<ElectrumXNode> _getCurrentNode() async {
final node = NodeService().getPrimaryNodeFor(coin: coin) ??
final node = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
return ElectrumXNode(

View file

@ -12,7 +12,6 @@ import 'package:crypto/crypto.dart';
import 'package:decimal/decimal.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
@ -1359,7 +1358,7 @@ class LitecoinWallet extends CoinServiceAPI {
CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1371,7 +1370,7 @@ class LitecoinWallet extends CoinServiceAPI {
required CachedElectrumX cachedClient,
required TransactionNotificationTracker tracker,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore,
required SecureStorageInterface secureStore,
}) {
txTracker = tracker;
_walletId = walletId;
@ -1381,13 +1380,12 @@ class LitecoinWallet extends CoinServiceAPI {
_cachedElectrumXClient = cachedClient;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
@override
Future<void> updateNode(bool shouldRefresh) async {
final failovers = NodeService()
final failovers = NodeService(secureStorageInterface: _secureStore)
.failoverNodesFor(coin: coin)
.map((e) => ElectrumXNode(
address: e.host,
@ -1425,7 +1423,8 @@ class LitecoinWallet extends CoinServiceAPI {
}
Future<ElectrumXNode> getCurrentNode() async {
final node = NodeService().getPrimaryNodeFor(coin: coin) ??
final node = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
return ElectrumXNode(

View file

@ -23,11 +23,8 @@ import 'package:flutter_libmonero/core/key_service.dart';
import 'package:flutter_libmonero/core/wallet_creation_service.dart';
import 'package:flutter_libmonero/monero/monero.dart';
import 'package:flutter_libmonero/view_model/send/output.dart' as monero_output;
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:mutex/mutex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
@ -49,6 +46,7 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
const int MINIMUM_CONFIRMATIONS = 10;
@ -67,12 +65,13 @@ class MoneroWallet extends CoinServiceAPI {
Timer? moneroAutosaveTimer;
late Coin _coin;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
Future<NodeModel> getCurrentNode() async {
return NodeService().getPrimaryNodeFor(coin: coin) ??
return NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
}
@ -81,14 +80,13 @@ class MoneroWallet extends CoinServiceAPI {
required String walletName,
required Coin coin,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore}) {
required SecureStorageInterface secureStore}) {
_walletId = walletId;
_walletName = walletName;
_coin = coin;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
bool _shouldAutoSync = false;
@ -154,7 +152,7 @@ class MoneroWallet extends CoinServiceAPI {
try {
_height = (walletBase!.syncStatus as SyncingSyncStatus).height;
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
// Logging.instance.log("$e $s", level: LogLevel.Warning);
}
int blocksRemaining = -1;
@ -163,7 +161,7 @@ class MoneroWallet extends CoinServiceAPI {
blocksRemaining =
(walletBase!.syncStatus as SyncingSyncStatus).blocksLeft;
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
// Logging.instance.log("$e $s", level: LogLevel.Warning);
}
int currentHeight = _height + blocksRemaining;
if (_height == -1 || blocksRemaining == -1) {
@ -196,7 +194,7 @@ class MoneroWallet extends CoinServiceAPI {
try {
syncingHeight = (walletBase!.syncStatus as SyncingSyncStatus).height;
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
// Logging.instance.log("$e $s", level: LogLevel.Warning);
}
final cachedHeight =
DB.instance.get<dynamic>(boxName: walletId, key: "storedSyncingHeight")
@ -419,7 +417,7 @@ class MoneroWallet extends CoinServiceAPI {
try {
progress = (walletBase!.syncStatus!).progress();
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Warning);
// Logging.instance.log("$e $s", level: LogLevel.Warning);
}
await _fetchTransactionData();
@ -670,11 +668,9 @@ class MoneroWallet extends CoinServiceAPI {
"Attempted to overwrite mnemonic on generate new wallet!");
}
storage = const FlutterSecureStorage();
walletService =
monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
WalletInfo walletInfo;
WalletCredentials credentials;
try {
@ -708,8 +704,7 @@ class MoneroWallet extends CoinServiceAPI {
credentials.walletInfo = walletInfo;
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
secureStorage: _secureStore,
walletService: walletService,
keyService: keysStorage,
);
@ -787,11 +782,10 @@ class MoneroWallet extends CoinServiceAPI {
// Logging.instance.log("Caught in initializeWallet(): $e\n$s");
// return false;
// }
storage = const FlutterSecureStorage();
walletService =
monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
await _generateNewWallet();
// var password;
@ -833,11 +827,9 @@ class MoneroWallet extends CoinServiceAPI {
"Attempted to initialize an existing wallet using an unknown wallet ID!");
}
storage = const FlutterSecureStorage();
walletService =
monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
await _prefs.init();
final data =
@ -889,9 +881,8 @@ class MoneroWallet extends CoinServiceAPI {
bool longMutex = false;
// TODO: are these needed?
FlutterSecureStorage? storage;
WalletService? walletService;
SharedPreferences? prefs;
KeyService? keysStorage;
MoneroWalletBase? walletBase;
WalletCreationService? _walletCreationService;
@ -906,14 +897,8 @@ class MoneroWallet extends CoinServiceAPI {
required String name,
required WalletType type,
}) async {
Directory root = (await getApplicationDocumentsDirectory());
if (Platform.isIOS) {
root = (await getLibraryDirectory());
}
//
if (Platform.isLinux) {
root = Directory("${root.path}/.stackwallet");
}
Directory root = await StackFileSystem.applicationRootDirectory();
final prefix = walletTypeToString(type).toLowerCase();
final walletsDir = Directory('${root.path}/wallets');
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
@ -970,11 +955,9 @@ class MoneroWallet extends CoinServiceAPI {
await DB.instance
.put<dynamic>(boxName: walletId, key: "restoreHeight", value: height);
storage = const FlutterSecureStorage();
walletService =
monero.createMoneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
WalletInfo walletInfo;
WalletCredentials credentials;
String name = _walletId;
@ -1001,8 +984,7 @@ class MoneroWallet extends CoinServiceAPI {
credentials.walletInfo = walletInfo;
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
secureStorage: _secureStore,
walletService: walletService,
keyService: keysStorage,
);

View file

@ -12,7 +12,6 @@ import 'package:crypto/crypto.dart';
import 'package:decimal/decimal.dart';
import 'package:devicelocale/devicelocale.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
@ -1350,7 +1349,7 @@ class NamecoinWallet extends CoinServiceAPI {
CachedElectrumX get cachedElectrumXClient => _cachedElectrumXClient;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
@ -1362,7 +1361,7 @@ class NamecoinWallet extends CoinServiceAPI {
required CachedElectrumX cachedClient,
required TransactionNotificationTracker tracker,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore,
required SecureStorageInterface secureStore,
}) {
txTracker = tracker;
_walletId = walletId;
@ -1372,13 +1371,12 @@ class NamecoinWallet extends CoinServiceAPI {
_cachedElectrumXClient = cachedClient;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
@override
Future<void> updateNode(bool shouldRefresh) async {
final failovers = NodeService()
final failovers = NodeService(secureStorageInterface: _secureStore)
.failoverNodesFor(coin: coin)
.map((e) => ElectrumXNode(
address: e.host,
@ -1416,7 +1414,8 @@ class NamecoinWallet extends CoinServiceAPI {
}
Future<ElectrumXNode> getCurrentNode() async {
final node = NodeService().getPrimaryNodeFor(coin: coin) ??
final node = NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
return ElectrumXNode(

View file

@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_core/monero_transaction_priority.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pending_transaction.dart';
@ -25,11 +24,8 @@ import 'package:flutter_libmonero/core/wallet_creation_service.dart';
import 'package:flutter_libmonero/view_model/send/output.dart'
as wownero_output;
import 'package:flutter_libmonero/wownero/wownero.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:mutex/mutex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
@ -51,6 +47,7 @@ import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
const int MINIMUM_CONFIRMATIONS = 10;
@ -69,12 +66,13 @@ class WowneroWallet extends CoinServiceAPI {
Timer? wowneroAutosaveTimer;
late Coin _coin;
late FlutterSecureStorageInterface _secureStore;
late SecureStorageInterface _secureStore;
late PriceAPI _priceAPI;
Future<NodeModel> getCurrentNode() async {
return NodeService().getPrimaryNodeFor(coin: coin) ??
return NodeService(secureStorageInterface: _secureStore)
.getPrimaryNodeFor(coin: coin) ??
DefaultNodes.getNodeFor(coin);
}
@ -83,14 +81,13 @@ class WowneroWallet extends CoinServiceAPI {
required String walletName,
required Coin coin,
PriceAPI? priceAPI,
FlutterSecureStorageInterface? secureStore}) {
required SecureStorageInterface secureStore}) {
_walletId = walletId;
_walletName = walletName;
_coin = coin;
_priceAPI = priceAPI ?? PriceAPI(Client());
_secureStore =
secureStore ?? const SecureStorageWrapper(FlutterSecureStorage());
_secureStore = secureStore;
}
bool _shouldAutoSync = false;
@ -672,12 +669,10 @@ class WowneroWallet extends CoinServiceAPI {
"Attempted to overwrite mnemonic on generate new wallet!");
}
storage = const FlutterSecureStorage();
// TODO: Wallet Service may need to be switched to Wownero
walletService =
wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
WalletInfo walletInfo;
WalletCredentials credentials;
try {
@ -702,8 +697,7 @@ class WowneroWallet extends CoinServiceAPI {
credentials.walletInfo = walletInfo;
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
secureStorage: _secureStore,
walletService: walletService,
keyService: keysStorage,
);
@ -793,11 +787,9 @@ class WowneroWallet extends CoinServiceAPI {
// Logging.instance.log("Caught in initializeWallet(): $e\n$s");
// return false;
// }
storage = const FlutterSecureStorage();
walletService =
wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
await _generateNewWallet(seedWordsLength: seedWordsLength);
// var password;
@ -839,11 +831,9 @@ class WowneroWallet extends CoinServiceAPI {
"Attempted to initialize an existing wallet using an unknown wallet ID!");
}
storage = const FlutterSecureStorage();
walletService =
wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
await _prefs.init();
final data =
@ -895,9 +885,8 @@ class WowneroWallet extends CoinServiceAPI {
bool longMutex = false;
// TODO: are these needed?
FlutterSecureStorage? storage;
WalletService? walletService;
SharedPreferences? prefs;
KeyService? keysStorage;
WowneroWalletBase? walletBase;
WalletCreationService? _walletCreationService;
@ -912,13 +901,8 @@ class WowneroWallet extends CoinServiceAPI {
required String name,
required WalletType type,
}) async {
Directory root = (await getApplicationDocumentsDirectory());
if (Platform.isIOS) {
root = (await getLibraryDirectory());
}
if (Platform.isLinux) {
root = Directory("${root.path}/.stackwallet");
}
Directory root = await StackFileSystem.applicationRootDirectory();
final prefix = walletTypeToString(type).toLowerCase();
final walletsDir = Directory('${root.path}/wallets');
final walletDire = Directory('${walletsDir.path}/$prefix/$name');
@ -993,11 +977,9 @@ class WowneroWallet extends CoinServiceAPI {
await DB.instance
.put<dynamic>(boxName: walletId, key: "restoreHeight", value: height);
storage = const FlutterSecureStorage();
walletService =
wownero.createWowneroWalletService(DB.instance.moneroWalletInfoBox);
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
keysStorage = KeyService(_secureStore);
WalletInfo walletInfo;
WalletCredentials credentials;
String name = _walletId;
@ -1024,8 +1006,7 @@ class WowneroWallet extends CoinServiceAPI {
credentials.walletInfo = walletInfo;
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
secureStorage: _secureStore,
walletService: walletService,
keyService: keysStorage,
);

View file

@ -1,7 +1,6 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/models/node_model.dart';
@ -13,13 +12,11 @@ import 'package:stackwallet/utilities/logger.dart';
const kStackCommunityNodesEndpoint = "https://extras.stackwallet.com";
class NodeService extends ChangeNotifier {
final FlutterSecureStorageInterface secureStorageInterface;
final SecureStorageInterface secureStorageInterface;
/// Exposed [secureStorageInterface] in order to inject mock for tests
NodeService({
this.secureStorageInterface = const SecureStorageWrapper(
FlutterSecureStorage(),
),
required this.secureStorageInterface,
});
Future<void> updateDefaults() async {

View file

@ -205,13 +205,14 @@ class Wallets extends ChangeNotifier {
final txTracker =
TransactionNotificationTracker(walletId: walletId);
final failovers = NodeService().failoverNodesFor(coin: coin);
final failovers = nodeService.failoverNodesFor(coin: coin);
// load wallet
final wallet = CoinServiceAPI.from(
coin,
walletId,
entry.value.name,
nodeService.secureStorageInterface,
node,
txTracker,
prefs,

View file

@ -3,7 +3,6 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_libmonero/monero/monero.dart';
import 'package:flutter_libmonero/wownero/wownero.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/notifications_service.dart';
@ -48,17 +47,14 @@ class WalletInfo {
}
class WalletsService extends ChangeNotifier {
late final FlutterSecureStorageInterface _secureStore;
late final SecureStorageInterface _secureStore;
Future<Map<String, WalletInfo>>? _walletNames;
Future<Map<String, WalletInfo>> get walletNames =>
_walletNames ??= _fetchWalletNames();
WalletsService({
FlutterSecureStorageInterface secureStorageInterface =
const SecureStorageWrapper(
FlutterSecureStorage(),
),
required SecureStorageInterface secureStorageInterface,
}) {
_secureStore = secureStorageInterface;
}

View file

@ -59,6 +59,7 @@ class _SVG {
String txExchangeFailed(BuildContext context) =>
"assets/svg/${Theme.of(context).extension<StackColors>()!.themeType.name}/tx-exchange-icon-failed.svg";
String get circlePlus => "assets/svg/plus-circle.svg";
String get framedGear => "assets/svg/framed-gear.svg";
String get framedAddressBook => "assets/svg/framed-address-book.svg";
String get themeLight => "assets/svg/light/light-mode.svg";

View file

@ -1,4 +1,3 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/hive/db.dart';
@ -17,9 +16,7 @@ import 'package:stackwallet/utilities/prefs.dart';
class DbVersionMigrator {
Future<void> migrate(
int fromVersion, {
FlutterSecureStorageInterface secureStore = const SecureStorageWrapper(
FlutterSecureStorage(),
),
required SecureStorageInterface secureStore,
}) async {
Logging.instance.log(
"Running migrate fromVersion $fromVersion",
@ -29,8 +26,9 @@ class DbVersionMigrator {
case 0:
await Hive.openBox<dynamic>(DB.boxNameAllWalletsData);
await Hive.openBox<dynamic>(DB.boxNamePrefs);
final walletsService = WalletsService();
final nodeService = NodeService();
final walletsService =
WalletsService(secureStorageInterface: secureStore);
final nodeService = NodeService(secureStorageInterface: secureStore);
final prefs = Prefs.instance;
final walletInfoList = await walletsService.walletNames;
await prefs.init();
@ -118,7 +116,7 @@ class DbVersionMigrator {
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 1);
// try to continue migrating
return await migrate(1);
return await migrate(1, secureStore: secureStore);
case 1:
await Hive.openBox<ExchangeTransaction>(DB.boxNameTrades);
@ -142,7 +140,7 @@ class DbVersionMigrator {
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 2);
// try to continue migrating
return await migrate(2);
return await migrate(2, secureStore: secureStore);
case 2:
await Hive.openBox<dynamic>(DB.boxNamePrefs);
final prefs = Prefs.instance;
@ -154,7 +152,7 @@ class DbVersionMigrator {
// update version
await DB.instance.put<dynamic>(
boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 3);
return await migrate(3);
return await migrate(3, secureStore: secureStore);
default:
// finally return

View file

@ -1,6 +1,6 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:stack_wallet_backup/secure_storage.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/hive/db.dart';
import 'package:stackwallet/utilities/logger.dart';
const String _kKeyBlobKey = "swbKeyBlobKeyStringID";
@ -24,7 +24,6 @@ String _getMessageFromException(Object exception) {
class DPS {
StorageCryptoHandler? _handler;
final SecureStorageWrapper secureStorageWrapper;
StorageCryptoHandler get handler {
if (_handler == null) {
@ -34,11 +33,7 @@ class DPS {
return _handler!;
}
DPS({
this.secureStorageWrapper = const SecureStorageWrapper(
FlutterSecureStorage(),
),
});
DPS();
Future<void> initFromNew(String passphrase) async {
if (_handler != null) {
@ -47,10 +42,14 @@ class DPS {
try {
_handler = await StorageCryptoHandler.fromNewPassphrase(passphrase);
await secureStorageWrapper.write(
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
await DB.instance.put<String>(
boxName: DB.boxNameDesktopData,
key: _kKeyBlobKey,
value: await _handler!.getKeyBlob(),
);
await box.close();
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
@ -65,7 +64,13 @@ class DPS {
throw Exception(
"DPS: attempted to re initialize with existing passphrase");
}
final keyBlob = await secureStorageWrapper.read(key: _kKeyBlobKey);
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
final keyBlob = DB.instance.get<String>(
boxName: DB.boxNameDesktopData,
key: _kKeyBlobKey,
);
await box.close();
if (keyBlob == null) {
throw Exception(
@ -79,11 +84,84 @@ class DPS {
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Error,
);
rethrow;
throw Exception(_getMessageFromException(e));
}
}
Future<bool> verifyPassphrase(String passphrase) async {
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
final keyBlob = DB.instance.get<String>(
boxName: DB.boxNameDesktopData,
key: _kKeyBlobKey,
);
await box.close();
if (keyBlob == null) {
// no passphrase key blob found so any passphrase is technically bad
return false;
}
try {
await StorageCryptoHandler.fromExisting(passphrase, keyBlob);
// existing passphrase matches key blob
return true;
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Warning,
);
// password is wrong or some other error
return false;
}
}
Future<bool> changePassphrase(
String passphraseOld,
String passphraseNew,
) async {
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
final keyBlob = DB.instance.get<String>(
boxName: DB.boxNameDesktopData,
key: _kKeyBlobKey,
);
await box.close();
if (keyBlob == null) {
// no passphrase key blob found so any passphrase is technically bad
return false;
}
if (!(await verifyPassphrase(passphraseOld))) {
return false;
}
try {
await _handler!.resetPassphrase(passphraseNew);
final box = await Hive.openBox<String>(DB.boxNameDesktopData);
await DB.instance.put<String>(
boxName: DB.boxNameDesktopData,
key: _kKeyBlobKey,
value: await _handler!.getKeyBlob(),
);
await box.close();
// successfully updated passphrase
return true;
} catch (e, s) {
Logging.instance.log(
"${_getMessageFromException(e)}\n$s",
level: LogLevel.Warning,
);
return false;
}
}
Future<bool> hasPassword() async {
return (await secureStorageWrapper.read(key: _kKeyBlobKey)) != null;
final keyBlob = DB.instance.get<String>(
boxName: DB.boxNameDesktopData,
key: _kKeyBlobKey,
);
return keyBlob != null;
}
}

View file

@ -1,6 +1,12 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:isar/isar.dart';
import 'package:stack_wallet_backup/secure_storage.dart';
import 'package:stackwallet/models/isar/models/encrypted_string_value.dart';
import 'package:stackwallet/utilities/stack_file_system.dart';
abstract class SecureStorageInterface {
dynamic get store;
abstract class FlutterSecureStorageInterface {
Future<void> write({
required String key,
required String? value,
@ -33,10 +39,82 @@ abstract class FlutterSecureStorageInterface {
});
}
class SecureStorageWrapper implements FlutterSecureStorageInterface {
final FlutterSecureStorage secureStore;
class DesktopSecureStore {
final StorageCryptoHandler handler;
late final Isar isar;
const SecureStorageWrapper(this.secureStore);
DesktopSecureStore(this.handler);
Future<void> init() async {
isar = await Isar.open(
[EncryptedStringValueSchema],
directory: (await StackFileSystem.applicationIsarDirectory()).path,
inspector: false,
name: "desktopStore",
);
}
Future<String?> read({
required String key,
}) async {
final value =
await isar.encryptedStringValues.filter().keyEqualTo(key).findFirst();
// value does not exist;
if (value == null) {
return null;
}
return await handler.decryptValue(key, value.value);
}
Future<void> write({
required String key,
required String? value,
}) async {
if (value == null) {
// here we assume that a value is to be deleted
await isar.writeTxn(() async {
await isar.encryptedStringValues.deleteByKey(key);
});
} else {
// otherwise created encrypted object value
final object = EncryptedStringValue();
object.key = key;
object.value = await handler.encryptValue(key, value);
// store object value
await isar.writeTxn(() async {
await isar.encryptedStringValues.put(object);
});
}
}
Future<void> delete({
required String key,
}) async {
await isar.writeTxn(() async {
await isar.encryptedStringValues.deleteByKey(key);
});
}
}
/// all *Options params ignored on desktop
class SecureStorageWrapper implements SecureStorageInterface {
final dynamic _store;
final bool _isDesktop;
@override
dynamic get store => _store;
const SecureStorageWrapper({
required dynamic store,
required bool isDesktop,
}) : assert(isDesktop
? store is DesktopSecureStore
: store is FlutterSecureStorage),
_store = store,
_isDesktop = isDesktop;
@override
Future<String?> read({
@ -47,16 +125,20 @@ class SecureStorageWrapper implements FlutterSecureStorageInterface {
WebOptions? webOptions,
MacOsOptions? mOptions,
WindowsOptions? wOptions,
}) {
return secureStore.read(
key: key,
iOptions: iOptions,
aOptions: aOptions,
lOptions: lOptions,
webOptions: webOptions,
mOptions: mOptions,
wOptions: wOptions,
);
}) async {
if (_isDesktop) {
return await (_store as DesktopSecureStore).read(key: key);
} else {
return await (_store as FlutterSecureStorage).read(
key: key,
iOptions: iOptions,
aOptions: aOptions,
lOptions: lOptions,
webOptions: webOptions,
mOptions: mOptions,
wOptions: wOptions,
);
}
}
@override
@ -69,17 +151,21 @@ class SecureStorageWrapper implements FlutterSecureStorageInterface {
WebOptions? webOptions,
MacOsOptions? mOptions,
WindowsOptions? wOptions,
}) {
return secureStore.write(
key: key,
value: value,
iOptions: iOptions,
aOptions: aOptions,
lOptions: lOptions,
webOptions: webOptions,
mOptions: mOptions,
wOptions: wOptions,
);
}) async {
if (_isDesktop) {
return await (_store as DesktopSecureStore).write(key: key, value: value);
} else {
return await (_store as FlutterSecureStorage).write(
key: key,
value: value,
iOptions: iOptions,
aOptions: aOptions,
lOptions: lOptions,
webOptions: webOptions,
mOptions: mOptions,
wOptions: wOptions,
);
}
}
@override
@ -92,20 +178,24 @@ class SecureStorageWrapper implements FlutterSecureStorageInterface {
MacOsOptions? mOptions,
WindowsOptions? wOptions,
}) async {
await secureStore.delete(
key: key,
iOptions: iOptions,
aOptions: aOptions,
lOptions: lOptions,
webOptions: webOptions,
mOptions: mOptions,
wOptions: wOptions,
);
if (_isDesktop) {
return (_store as DesktopSecureStore).delete(key: key);
} else {
return await (_store as FlutterSecureStorage).delete(
key: key,
iOptions: iOptions,
aOptions: aOptions,
lOptions: lOptions,
webOptions: webOptions,
mOptions: mOptions,
wOptions: wOptions,
);
}
}
}
// Mock class for testing purposes
class FakeSecureStorage implements FlutterSecureStorageInterface {
class FakeSecureStorage implements SecureStorageInterface {
final Map<String, String?> _store = {};
int _interactions = 0;
int get interactions => _interactions;
@ -161,4 +251,7 @@ class FakeSecureStorage implements FlutterSecureStorageInterface {
_deletes++;
_store.remove(key);
}
@override
dynamic get store => throw UnimplementedError();
}

View file

@ -0,0 +1,66 @@
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/util.dart';
abstract class StackFileSystem {
static Future<Directory> applicationRootDirectory() async {
Directory appDirectory;
// todo: can merge and do same as regular linux home dir?
if (Logging.isArmLinux) {
appDirectory = await getApplicationDocumentsDirectory();
appDirectory = Directory("${appDirectory.path}/.stackwallet");
} else if (Platform.isLinux) {
appDirectory = Directory("${Platform.environment['HOME']}/.stackwallet");
} else if (Platform.isWindows) {
// TODO: windows root .stackwallet dir location
throw Exception("Unsupported platform");
} else if (Platform.isMacOS) {
// currently run in ipad mode??
throw Exception("Unsupported platform");
} else if (Platform.isIOS) {
// todo: check if we need different behaviour here
if (Util.isDesktop) {
appDirectory = await getLibraryDirectory();
} else {
appDirectory = await getLibraryDirectory();
}
} else if (Platform.isAndroid) {
appDirectory = await getApplicationDocumentsDirectory();
} else {
throw Exception("Unsupported platform");
}
if (!appDirectory.existsSync()) {
await appDirectory.create(recursive: true);
}
return appDirectory;
}
static Future<Directory> applicationIsarDirectory() async {
final root = await applicationRootDirectory();
if (Util.isDesktop) {
final dir = Directory("${root.path}/isar");
if (!dir.existsSync()) {
await dir.create();
}
return dir;
} else {
return root;
}
}
static Future<Directory> applicationHiveDirectory() async {
final root = await applicationRootDirectory();
if (Util.isDesktop) {
final dir = Directory("${root.path}/hive");
if (!dir.existsSync()) {
await dir.create();
}
return dir;
} else {
return root;
}
}
}

View file

@ -21,3 +21,27 @@ class ConditionalParent extends StatelessWidget {
}
}
}
class BranchedParent extends StatelessWidget {
const BranchedParent({
Key? key,
required this.condition,
required this.conditionBranchBuilder,
required this.otherBranchBuilder,
required this.children,
}) : super(key: key);
final bool condition;
final Widget Function(List<Widget>) conditionBranchBuilder;
final Widget Function(List<Widget>) otherBranchBuilder;
final List<Widget> children;
@override
Widget build(BuildContext context) {
if (condition) {
return conditionBranchBuilder(children);
} else {
return otherBranchBuilder(children);
}
}
}

View file

@ -1244,7 +1244,7 @@ packages:
source: hosted
version: "3.0.1"
shared_preferences:
dependency: "direct main"
dependency: transitive
description:
name: shared_preferences
url: "https://pub.dartlang.org"

View file

@ -126,7 +126,7 @@ dependencies:
pointycastle: ^3.6.0
package_info_plus: ^1.4.2
lottie: ^1.3.0
shared_preferences: ^2.0.15
# shared_preferences: ^2.0.15
file_picker: ^5.0.1
connectivity_plus: 2.3.6+1
# document_file_save_plus: ^1.0.5
@ -314,6 +314,7 @@ flutter:
- assets/svg/exit-desktop.svg
- assets/svg/keys.svg
- assets/svg/arrow-down.svg
- assets/svg/plus-circle.svg
# coin icons
- assets/svg/coin_icons/Bitcoin.svg
- assets/svg/coin_icons/Litecoin.svg

View file

@ -678,6 +678,14 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs {
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);
@override
_i4.Future<bool> isExternalCallsSet() => (super.noSuchMethod(
Invocation.method(
#isExternalCallsSet,
[],
),
returnValue: _i4.Future<bool>.value(false),
) as _i4.Future<bool>);
@override
void addListener(_i9.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,

View file

@ -399,6 +399,14 @@ class MockPrefs extends _i1.Mock implements _i4.Prefs {
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i3.Future<bool> isExternalCallsSet() => (super.noSuchMethod(
Invocation.method(
#isExternalCallsSet,
[],
),
returnValue: _i3.Future<bool>.value(false),
) as _i3.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,

View file

@ -13,7 +13,7 @@ void main() {
when(secureStore.write(key: "testKey", value: "some value"))
.thenAnswer((_) async => null);
final wrapper = SecureStorageWrapper(secureStore);
final wrapper = SecureStorageWrapper(store: secureStore, isDesktop: false);
await expectLater(
() async => await wrapper.write(key: "testKey", value: "some value"),
@ -27,7 +27,7 @@ void main() {
final secureStore = MockFlutterSecureStorage();
when(secureStore.read(key: "testKey"))
.thenAnswer((_) async => "some value");
final wrapper = SecureStorageWrapper(secureStore);
final wrapper = SecureStorageWrapper(store: secureStore, isDesktop: false);
final result = await wrapper.read(key: "testKey");
@ -40,7 +40,7 @@ void main() {
test("SecureStorageWrapper delete", () async {
final secureStore = MockFlutterSecureStorage();
when(secureStore.delete(key: "testKey")).thenAnswer((_) async {});
final wrapper = SecureStorageWrapper(secureStore);
final wrapper = SecureStorageWrapper(store: secureStore, isDesktop: false);
await expectLater(
() async => await wrapper.delete(key: "testKey"), returnsNormally);

View file

@ -1,5 +1,5 @@
// Mocks generated by Mockito 5.3.2 from annotations
// in stackwallet/test/pages/send_view_test.dart.
// in stackwallet/test/pages/send_view/send_view_test.dart.
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
@ -86,7 +86,7 @@ class _FakeManager_3 extends _i1.SmartFake implements _i6.Manager {
}
class _FakeFlutterSecureStorageInterface_4 extends _i1.SmartFake
implements _i7.FlutterSecureStorageInterface {
implements _i7.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_4(
Object parent,
Invocation parentInvocation,
@ -621,14 +621,13 @@ class MockNodeService extends _i1.Mock implements _i3.NodeService {
}
@override
_i7.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i7.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_4(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i7.FlutterSecureStorageInterface);
) as _i7.SecureStorageInterface);
@override
List<_i19.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),
@ -890,6 +889,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i20.BitcoinWallet {
returnValueForMissingStub: null,
);
@override
set cachedTxData(_i9.TransactionData? _cachedTxData) => super.noSuchMethod(
Invocation.setter(
#cachedTxData,
_cachedTxData,
),
returnValueForMissingStub: null,
);
@override
bool get isActive => (super.noSuchMethod(
Invocation.getter(#isActive),
returnValue: false,
@ -1272,6 +1279,16 @@ class MockBitcoinWallet extends _i1.Mock implements _i20.BitcoinWallet {
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
@override
_i16.Future<void> updateSentCachedTxData(Map<String, dynamic>? txData) =>
(super.noSuchMethod(
Invocation.method(
#updateSentCachedTxData,
[txData],
),
returnValue: _i16.Future<void>.value(),
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
@override
bool validateAddress(String? address) => (super.noSuchMethod(
Invocation.method(
#validateAddress,
@ -1875,6 +1892,14 @@ class MockPrefs extends _i1.Mock implements _i17.Prefs {
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
@override
_i16.Future<bool> isExternalCallsSet() => (super.noSuchMethod(
Invocation.method(
#isExternalCallsSet,
[],
),
returnValue: _i16.Future<bool>.value(false),
) as _i16.Future<bool>);
@override
void addListener(_i18.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,
@ -2623,4 +2648,14 @@ class MockCoinServiceAPI extends _i1.Mock implements _i13.CoinServiceAPI {
),
returnValue: _i16.Future<bool>.value(false),
) as _i16.Future<bool>);
@override
_i16.Future<void> updateSentCachedTxData(Map<String, dynamic>? txData) =>
(super.noSuchMethod(
Invocation.method(
#updateSentCachedTxData,
[txData],
),
returnValue: _i16.Future<void>.value(),
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
}

View file

@ -350,6 +350,14 @@ class MockPrefs extends _i1.Mock implements _i3.Prefs {
returnValueForMissingStub: _i7.Future<void>.value(),
) as _i7.Future<void>);
@override
_i7.Future<bool> isExternalCallsSet() => (super.noSuchMethod(
Invocation.method(
#isExternalCallsSet,
[],
),
returnValue: _i7.Future<bool>.value(false),
) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,

View file

@ -30,7 +30,7 @@ import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
// ignore_for_file: subtype_of_sealed_class
class _FakeFlutterSecureStorageInterface_0 extends _i1.SmartFake
implements _i2.FlutterSecureStorageInterface {
implements _i2.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_0(
Object parent,
Invocation parentInvocation,
@ -309,14 +309,13 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService {
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i10.NodeService {
@override
_i2.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_0(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i2.FlutterSecureStorageInterface);
) as _i2.SecureStorageInterface);
@override
List<_i11.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),

View file

@ -30,7 +30,7 @@ import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
// ignore_for_file: subtype_of_sealed_class
class _FakeFlutterSecureStorageInterface_0 extends _i1.SmartFake
implements _i2.FlutterSecureStorageInterface {
implements _i2.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_0(
Object parent,
Invocation parentInvocation,
@ -309,14 +309,13 @@ class MockWalletsService extends _i1.Mock implements _i6.WalletsService {
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i10.NodeService {
@override
_i2.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_0(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i2.FlutterSecureStorageInterface);
) as _i2.SecureStorageInterface);
@override
List<_i11.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),

View file

@ -84,7 +84,7 @@ class _FakeTransactionData_4 extends _i1.SmartFake
}
class _FakeFlutterSecureStorageInterface_5 extends _i1.SmartFake
implements _i6.FlutterSecureStorageInterface {
implements _i6.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_5(
Object parent,
Invocation parentInvocation,
@ -744,14 +744,14 @@ class MockManager extends _i1.Mock implements _i12.Manager {
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i13.NodeService {
@override
_i6.FlutterSecureStorageInterface get secureStorageInterface =>
_i6.SecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_5(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i6.FlutterSecureStorageInterface);
) as _i6.SecureStorageInterface);
@override
List<_i14.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),

View file

@ -29,7 +29,7 @@ import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
// ignore_for_file: subtype_of_sealed_class
class _FakeFlutterSecureStorageInterface_0 extends _i1.SmartFake
implements _i2.FlutterSecureStorageInterface {
implements _i2.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_0(
Object parent,
Invocation parentInvocation,
@ -86,14 +86,13 @@ class _FakeTransactionData_4 extends _i1.SmartFake
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i6.NodeService {
@override
_i2.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_0(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i2.FlutterSecureStorageInterface);
) as _i2.SecureStorageInterface);
@override
List<_i7.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),

View file

@ -29,7 +29,7 @@ import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
// ignore_for_file: subtype_of_sealed_class
class _FakeFlutterSecureStorageInterface_0 extends _i1.SmartFake
implements _i2.FlutterSecureStorageInterface {
implements _i2.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_0(
Object parent,
Invocation parentInvocation,
@ -86,14 +86,13 @@ class _FakeTransactionData_4 extends _i1.SmartFake
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i6.NodeService {
@override
_i2.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_0(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i2.FlutterSecureStorageInterface);
) as _i2.SecureStorageInterface);
@override
List<_i7.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),

View file

@ -25,7 +25,7 @@ import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
// ignore_for_file: subtype_of_sealed_class
class _FakeFlutterSecureStorageInterface_0 extends _i1.SmartFake
implements _i2.FlutterSecureStorageInterface {
implements _i2.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_0(
Object parent,
Invocation parentInvocation,
@ -40,14 +40,13 @@ class _FakeFlutterSecureStorageInterface_0 extends _i1.SmartFake
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override
_i2.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i2.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_0(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i2.FlutterSecureStorageInterface);
) as _i2.SecureStorageInterface);
@override
List<_i4.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),

View file

@ -139,6 +139,22 @@ class MockCachedElectrumX extends _i1.Mock implements _i6.CachedElectrumX {
_i8.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i8.Future<Map<String, dynamic>>);
@override
String base64ToHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToHex,
[source],
),
returnValue: '',
) as String);
@override
String base64ToReverseHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToReverseHex,
[source],
),
returnValue: '',
) as String);
@override
_i8.Future<Map<String, dynamic>> getTransaction({
required String? txHash,
required _i9.Coin? coin,

View file

@ -103,7 +103,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinWallet? testnetWallet;
@ -194,7 +194,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinWallet? mainnetWallet;
@ -363,7 +363,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinWallet? btc;
@ -428,7 +428,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinWallet? btc;
@ -640,7 +640,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinWallet? btc;

View file

@ -465,6 +465,22 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX {
_i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i6.Future<Map<String, dynamic>>);
@override
String base64ToHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToHex,
[source],
),
returnValue: '',
) as String);
@override
String base64ToReverseHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToReverseHex,
[source],
),
returnValue: '',
) as String);
@override
_i6.Future<Map<String, dynamic>> getTransaction({
required String? txHash,
required _i8.Coin? coin,

View file

@ -64,7 +64,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinCashWallet? mainnetWallet;
@ -203,7 +203,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinCashWallet? mainnetWallet;
@ -314,7 +314,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinCashWallet? bch;
@ -383,7 +383,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinCashWallet? bch;
@ -606,7 +606,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
BitcoinCashWallet? bch;

View file

@ -465,6 +465,22 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX {
_i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i6.Future<Map<String, dynamic>>);
@override
String base64ToHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToHex,
[source],
),
returnValue: '',
) as String);
@override
String base64ToReverseHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToReverseHex,
[source],
),
returnValue: '',
) as String);
@override
_i6.Future<Map<String, dynamic>> getTransaction({
required String? txHash,
required _i8.Coin? coin,

View file

@ -97,7 +97,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
DogecoinWallet? mainnetWallet;
@ -196,7 +196,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
DogecoinWallet? doge;
@ -266,7 +266,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
DogecoinWallet? doge;
@ -489,7 +489,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
DogecoinWallet? doge;

View file

@ -465,6 +465,22 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX {
_i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i6.Future<Map<String, dynamic>>);
@override
String base64ToHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToHex,
[source],
),
returnValue: '',
) as String);
@override
String base64ToReverseHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToReverseHex,
[source],
),
returnValue: '',
) as String);
@override
_i6.Future<Map<String, dynamic>> getTransaction({
required String? txHash,
required _i8.Coin? coin,

View file

@ -465,6 +465,22 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX {
_i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i6.Future<Map<String, dynamic>>);
@override
String base64ToHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToHex,
[source],
),
returnValue: '',
) as String);
@override
String base64ToReverseHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToReverseHex,
[source],
),
returnValue: '',
) as String);
@override
_i6.Future<Map<String, dynamic>> getTransaction({
required String? txHash,
required _i8.Coin? coin,

View file

@ -117,6 +117,14 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValueForMissingStub: null,
);
@override
set cachedTxData(_i4.TransactionData? _cachedTxData) => super.noSuchMethod(
Invocation.setter(
#cachedTxData,
_cachedTxData,
),
returnValueForMissingStub: null,
);
@override
_i2.TransactionNotificationTracker get txTracker => (super.noSuchMethod(
Invocation.getter(#txTracker),
returnValue: _FakeTransactionNotificationTracker_0(
@ -375,6 +383,16 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValue: false,
) as bool);
@override
_i8.Future<void> updateSentCachedTxData(Map<String, dynamic>? txData) =>
(super.noSuchMethod(
Invocation.method(
#updateSentCachedTxData,
[txData],
),
returnValue: _i8.Future<void>.value(),
returnValueForMissingStub: _i8.Future<void>.value(),
) as _i8.Future<void>);
@override
_i8.Future<bool> testNetworkConnection() => (super.noSuchMethod(
Invocation.method(
#testNetworkConnection,

View file

@ -1,5 +1,4 @@
import 'dart:core';
import 'dart:core' as core;
import 'dart:io';
import 'dart:math';
@ -19,18 +18,13 @@ import 'package:hive/hive.dart';
import 'package:hive_test/hive_test.dart';
import 'package:mockito/annotations.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stackwallet/services/wallets.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
// TODO trim down to the minimum imports above
import 'monero_wallet_test_data.dart';
//FlutterSecureStorage? storage;
FakeSecureStorage? storage;
WalletService? walletService;
SharedPreferences? prefs;
KeyService? keysStorage;
MoneroWalletBase? walletBase;
late WalletCreationService _walletCreationService;
@ -46,7 +40,6 @@ WalletType type = WalletType.monero;
@GenerateMocks([])
void main() async {
storage = FakeSecureStorage();
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
WalletInfo walletInfo = WalletInfo.external(
id: '',
@ -90,12 +83,12 @@ void main() async {
final dirPath = await pathForWalletDir(name: name, type: type);
path = await pathForWallet(name: name, type: type);
credentials =
// // creating a new wallet
// monero.createMoneroNewWalletCredentials(
// name: name, language: "English");
// restoring a previous wallet
monero.createMoneroRestoreWalletFromSeedCredentials(
name: name, height: 2580000, mnemonic: testMnemonic);
// // creating a new wallet
// monero.createMoneroNewWalletCredentials(
// name: name, language: "English");
// restoring a previous wallet
monero.createMoneroRestoreWalletFromSeedCredentials(
name: name, height: 2580000, mnemonic: testMnemonic);
walletInfo = WalletInfo.external(
id: WalletBase.idFor(name, type),
@ -111,7 +104,6 @@ void main() async {
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
walletService: walletService,
keyService: keysStorage,
);
@ -124,8 +116,8 @@ void main() async {
test("Test mainnet address generation from seed", () async {
final wallet = await
// _walletCreationService.create(credentials);
_walletCreationService.restoreFromSeed(credentials);
// _walletCreationService.create(credentials);
_walletCreationService.restoreFromSeed(credentials);
walletInfo.address = wallet.walletAddresses.address;
//print(walletInfo.address);
@ -134,8 +126,7 @@ void main() async {
walletBase = wallet as MoneroWalletBase;
//print("${walletBase?.seed}");
expect(
await walletBase!.validateAddress(walletInfo.address ?? ''), true);
expect(await walletBase!.validateAddress(walletInfo.address ?? ''), true);
// print(walletBase);
// loggerPrint(walletBase.toString());
@ -157,20 +148,31 @@ void main() async {
expect(
await walletBase!.getTransactionAddress(1, 2), mainnetTestData[1][2]);
expect(await walletBase!.validateAddress(''), false);
expect(
await walletBase!.validateAddress(''), false);
await walletBase!.validateAddress(
'4AeRgkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gp4nn'),
true);
expect(
await walletBase!.validateAddress('4AeRgkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gp4nn'), true);
await walletBase!.validateAddress(
'4asdfkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gpjkl'),
false);
expect(
await walletBase!.validateAddress('4asdfkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gpjkl'), false);
await walletBase!.validateAddress(
'8AeRgkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gp4nn'),
false);
expect(
await walletBase!.validateAddress('8AeRgkWZsMJhAWKMeCZ3h4ZSPnAcW5VBtRFyLd6gBEf6GgJU2FHXDA6i1DnQTd6h8R3VU5AkbGcWSNhtSwNNPgaD48gp4nn'), false);
await walletBase!.validateAddress(
'84kYPuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenvHy'),
true);
expect(
await walletBase!.validateAddress('84kYPuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenvHy'), true);
await walletBase!.validateAddress(
'8asdfuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenjkl'),
false);
expect(
await walletBase!.validateAddress('8asdfuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenjkl'), false);
expect(
await walletBase!.validateAddress('44kYPuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenvHy'), false);
await walletBase!.validateAddress(
'44kYPuZ1eaVKGQhf26QPNWbSLQG16BywXdLYYShVrPNMLAUAWce5vcpRc78FxwRphrG6Cda7faCKdUMr8fUCH3peHPenvHy'),
false);
});
});
/*
@ -229,6 +231,6 @@ Future<String> pathForWalletDir(
}
Future<String> pathForWallet(
{required String name, required WalletType type}) async =>
{required String name, required WalletType type}) async =>
await pathForWalletDir(name: name, type: type)
.then((path) => path + '/$name');

View file

@ -103,7 +103,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
NamecoinWallet? mainnetWallet;
@ -132,7 +132,7 @@ void main() {
mainnetWallet?.addressType(
address: "N673DDbjPcrNgJmrhJ1xQXF9LLizQzvjEs"),
DerivePathType.bip44);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
@ -144,7 +144,7 @@ void main() {
mainnetWallet?.addressType(
address: "nc1q6k4x8ye6865z3rc8zkt8gyu52na7njqt6hsk4v"),
DerivePathType.bip84);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
@ -156,7 +156,7 @@ void main() {
() => mainnetWallet?.addressType(
address: "tb1qzzlm6mnc8k54mx6akehl8p9ray8r439va5ndyq"),
throwsArgumentError);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
@ -168,7 +168,7 @@ void main() {
() => mainnetWallet?.addressType(
address: "mpMk94ETazqonHutyC1v6ajshgtP8oiFKU"),
throwsArgumentError);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
@ -180,7 +180,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
NamecoinWallet? nmc;
@ -208,7 +208,7 @@ void main() {
when(client?.ping()).thenAnswer((_) async => false);
final bool? result = await nmc?.testNetworkConnection();
expect(result, false);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verify(client?.ping()).called(1);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -219,7 +219,7 @@ void main() {
when(client?.ping()).thenThrow(Exception);
final bool? result = await nmc?.testNetworkConnection();
expect(result, false);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verify(client?.ping()).called(1);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -230,7 +230,7 @@ void main() {
when(client?.ping()).thenAnswer((_) async => true);
final bool? result = await nmc?.testNetworkConnection();
expect(result, true);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verify(client?.ping()).called(1);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -245,7 +245,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
NamecoinWallet? nmc;
@ -271,7 +271,7 @@ void main() {
test("get networkType main", () async {
expect(Coin.namecoin, Coin.namecoin);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -289,7 +289,7 @@ void main() {
secureStore: secureStore,
);
expect(Coin.namecoin, Coin.namecoin);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -297,7 +297,7 @@ void main() {
test("get cryptoCurrency", () async {
expect(Coin.namecoin, Coin.namecoin);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -305,7 +305,7 @@ void main() {
test("get coinName", () async {
expect(Coin.namecoin, Coin.namecoin);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -313,7 +313,7 @@ void main() {
test("get coinTicker", () async {
expect(Coin.namecoin, Coin.namecoin);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -323,7 +323,7 @@ void main() {
expect(Coin.namecoin, Coin.namecoin);
nmc?.walletName = "new name";
expect(nmc?.walletName, "new name");
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -338,7 +338,7 @@ void main() {
expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 1699), 712);
expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 2000), 712);
expect(nmc?.estimateTxFee(vSize: 356, feeRatePerKB: 12345), 4628);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -372,7 +372,7 @@ void main() {
verify(client?.estimateFee(blocks: 1)).called(1);
verify(client?.estimateFee(blocks: 5)).called(1);
verify(client?.estimateFee(blocks: 20)).called(1);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -409,7 +409,7 @@ void main() {
verify(client?.estimateFee(blocks: 1)).called(1);
verify(client?.estimateFee(blocks: 5)).called(1);
verify(client?.estimateFee(blocks: 20)).called(1);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -440,7 +440,7 @@ void main() {
// verify(client?.estimateFee(blocks: 1)).called(1);
// verify(client?.estimateFee(blocks: 5)).called(1);
// verify(client?.estimateFee(blocks: 20)).called(1);
// expect(secureStore?.interactions, 0);
// expect(secureStore.interactions, 0);
// verifyNoMoreInteractions(client);
// verifyNoMoreInteractions(cachedClient);
// verifyNoMoreInteractions(tracker);
@ -457,7 +457,7 @@ void main() {
MockElectrumX? client;
MockCachedElectrumX? cachedClient;
MockPriceAPI? priceAPI;
FakeSecureStorage? secureStore;
late FakeSecureStorage secureStore;
MockTransactionNotificationTracker? tracker;
NamecoinWallet? nmc;
@ -504,7 +504,7 @@ void main() {
// test("initializeWallet no network", () async {
// when(client?.ping()).thenAnswer((_) async => false);
// expect(await nmc?.initializeWallet(), false);
// expect(secureStore?.interactions, 0);
// expect(secureStore.interactions, 0);
// verify(client?.ping()).called(1);
// verifyNoMoreInteractions(client);
// verifyNoMoreInteractions(cachedClient);
@ -515,7 +515,7 @@ void main() {
// when(client?.ping()).thenThrow(Exception("Network connection failed"));
// final wallets = await Hive.openBox(testWalletId);
// expect(await nmc?.initializeExisting(), false);
// expect(secureStore?.interactions, 0);
// expect(secureStore.interactions, 0);
// verify(client?.ping()).called(1);
// verifyNoMoreInteractions(client);
// verifyNoMoreInteractions(cachedClient);
@ -539,7 +539,7 @@ void main() {
expectLater(() => nmc?.initializeExisting(), throwsA(isA<Exception>()))
.then((_) {
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
// verify(client?.ping()).called(1);
// verify(client?.getServerFeatures()).called(1);
verifyNoMoreInteractions(client);
@ -560,13 +560,13 @@ void main() {
"hash_function": "sha256",
"services": []
});
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_mnemonic", value: "some mnemonic");
final wallets = await Hive.openBox(testWalletId);
expectLater(() => nmc?.initializeExisting(), throwsA(isA<Exception>()))
.then((_) {
expect(secureStore?.interactions, 1);
expect(secureStore.interactions, 1);
// verify(client?.ping()).called(1);
// verify(client?.getServerFeatures()).called(1);
verifyNoMoreInteractions(client);
@ -603,7 +603,7 @@ void main() {
verify(client?.getServerFeatures()).called(1);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -623,7 +623,7 @@ void main() {
"services": []
});
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_mnemonic", value: "some mnemonic words");
bool hasThrown = false;
@ -640,7 +640,7 @@ void main() {
verify(client?.getServerFeatures()).called(1);
expect(secureStore?.interactions, 2);
expect(secureStore.interactions, 2);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -691,10 +691,10 @@ void main() {
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1);
expect(secureStore?.interactions, 20);
expect(secureStore?.writes, 7);
expect(secureStore?.reads, 13);
expect(secureStore?.deletes, 0);
expect(secureStore.interactions, 20);
expect(secureStore.writes, 7);
expect(secureStore.reads, 13);
expect(secureStore.deletes, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -814,10 +814,10 @@ void main() {
true);
}
expect(secureStore?.interactions, 14);
expect(secureStore?.writes, 7);
expect(secureStore?.reads, 7);
expect(secureStore?.deletes, 0);
expect(secureStore.interactions, 14);
expect(secureStore.writes, 7);
expect(secureStore.reads, 7);
expect(secureStore.deletes, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -911,17 +911,17 @@ void main() {
final preChangeIndexP2SH = await wallet.get('changeIndexP2SH');
final preChangeIndexP2WPKH = await wallet.get('changeIndexP2WPKH');
final preUtxoData = await wallet.get('latest_utxo_model');
final preReceiveDerivationsStringP2PKH = await secureStore?.read(
final preReceiveDerivationsStringP2PKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2PKH");
final preChangeDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_changeDerivationsP2PKH");
final preReceiveDerivationsStringP2SH = await secureStore?.read(
key: "${testWalletId}_receiveDerivationsP2SH");
final preChangeDerivationsStringP2PKH =
await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH");
final preReceiveDerivationsStringP2SH =
await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH");
final preChangeDerivationsStringP2SH =
await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
final preReceiveDerivationsStringP2WPKH = await secureStore?.read(
await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH");
final preReceiveDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2WPKH");
final preChangeDerivationsStringP2WPKH = await secureStore?.read(
final preChangeDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_changeDerivationsP2WPKH");
// destroy the data that the rescan will fix
@ -943,17 +943,17 @@ void main() {
await wallet.put('changeIndexP2PKH', 123);
await wallet.put('changeIndexP2SH', 123);
await wallet.put('changeIndexP2WPKH', 123);
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_receiveDerivationsP2PKH", value: "{}");
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_changeDerivationsP2PKH", value: "{}");
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_receiveDerivationsP2SH", value: "{}");
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_changeDerivationsP2SH", value: "{}");
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_receiveDerivationsP2WPKH", value: "{}");
await secureStore?.write(
await secureStore.write(
key: "${testWalletId}_changeDerivationsP2WPKH", value: "{}");
bool hasThrown = false;
@ -980,17 +980,17 @@ void main() {
final changeIndexP2SH = await wallet.get('changeIndexP2SH');
final changeIndexP2WPKH = await wallet.get('changeIndexP2WPKH');
final utxoData = await wallet.get('latest_utxo_model');
final receiveDerivationsStringP2PKH = await secureStore?.read(
final receiveDerivationsStringP2PKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2PKH");
final changeDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_changeDerivationsP2PKH");
final receiveDerivationsStringP2SH = await secureStore?.read(
key: "${testWalletId}_receiveDerivationsP2SH");
final changeDerivationsStringP2PKH =
await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH");
final receiveDerivationsStringP2SH =
await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH");
final changeDerivationsStringP2SH =
await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
final receiveDerivationsStringP2WPKH = await secureStore?.read(
await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH");
final receiveDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2WPKH");
final changeDerivationsStringP2WPKH = await secureStore?.read(
final changeDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_changeDerivationsP2WPKH");
expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH);
@ -1082,9 +1082,9 @@ void main() {
//
// argCount.forEach((key, value) => print("arg: $key\ncount: $value"));
expect(secureStore?.writes, 25);
expect(secureStore?.reads, 32);
expect(secureStore?.deletes, 6);
expect(secureStore.writes, 25);
expect(secureStore.reads, 32);
expect(secureStore.deletes, 6);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -1182,17 +1182,17 @@ void main() {
final preChangeIndexP2SH = await wallet.get('changeIndexP2SH');
final preChangeIndexP2WPKH = await wallet.get('changeIndexP2WPKH');
final preUtxoData = await wallet.get('latest_utxo_model');
final preReceiveDerivationsStringP2PKH = await secureStore?.read(
final preReceiveDerivationsStringP2PKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2PKH");
final preChangeDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_changeDerivationsP2PKH");
final preReceiveDerivationsStringP2SH = await secureStore?.read(
key: "${testWalletId}_receiveDerivationsP2SH");
final preChangeDerivationsStringP2PKH =
await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH");
final preReceiveDerivationsStringP2SH =
await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH");
final preChangeDerivationsStringP2SH =
await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
final preReceiveDerivationsStringP2WPKH = await secureStore?.read(
await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH");
final preReceiveDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2WPKH");
final preChangeDerivationsStringP2WPKH = await secureStore?.read(
final preChangeDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_changeDerivationsP2WPKH");
when(client?.getBatchHistory(args: historyBatchArgs0))
@ -1222,17 +1222,17 @@ void main() {
final changeIndexP2SH = await wallet.get('changeIndexP2SH');
final changeIndexP2WPKH = await wallet.get('changeIndexP2WPKH');
final utxoData = await wallet.get('latest_utxo_model');
final receiveDerivationsStringP2PKH = await secureStore?.read(
final receiveDerivationsStringP2PKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2PKH");
final changeDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_changeDerivationsP2PKH");
final receiveDerivationsStringP2SH = await secureStore?.read(
key: "${testWalletId}_receiveDerivationsP2SH");
final changeDerivationsStringP2PKH =
await secureStore.read(key: "${testWalletId}_changeDerivationsP2PKH");
final receiveDerivationsStringP2SH =
await secureStore.read(key: "${testWalletId}_receiveDerivationsP2SH");
final changeDerivationsStringP2SH =
await secureStore?.read(key: "${testWalletId}_changeDerivationsP2SH");
final receiveDerivationsStringP2WPKH = await secureStore?.read(
await secureStore.read(key: "${testWalletId}_changeDerivationsP2SH");
final receiveDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_receiveDerivationsP2WPKH");
final changeDerivationsStringP2WPKH = await secureStore?.read(
final changeDerivationsStringP2WPKH = await secureStore.read(
key: "${testWalletId}_changeDerivationsP2WPKH");
expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH);
@ -1296,9 +1296,9 @@ void main() {
verify(cachedClient?.clearSharedTransactionCache(coin: Coin.namecoin))
.called(1);
expect(secureStore?.writes, 19);
expect(secureStore?.reads, 32);
expect(secureStore?.deletes, 12);
expect(secureStore.writes, 19);
expect(secureStore.reads, 32);
expect(secureStore.deletes, 12);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -1366,21 +1366,21 @@ void main() {
height: 4000);
// modify addresses to properly mock data to build a tx
final rcv44 = await secureStore?.read(
final rcv44 = await secureStore.read(
key: testWalletId + "_receiveDerivationsP2PKH");
await secureStore?.write(
await secureStore.write(
key: testWalletId + "_receiveDerivationsP2PKH",
value: rcv44?.replaceFirst("1RMSPixoLPuaXuhR2v4HsUMcRjLncKDaw",
"16FuTPaeRSPVxxCnwQmdyx2PQWxX6HWzhQ"));
final rcv49 = await secureStore?.read(
key: testWalletId + "_receiveDerivationsP2SH");
await secureStore?.write(
final rcv49 =
await secureStore.read(key: testWalletId + "_receiveDerivationsP2SH");
await secureStore.write(
key: testWalletId + "_receiveDerivationsP2SH",
value: rcv49?.replaceFirst("3AV74rKfibWmvX34F99yEvUcG4LLQ9jZZk",
"36NvZTcMsMowbt78wPzJaHHWaNiyR73Y4g"));
final rcv84 = await secureStore?.read(
final rcv84 = await secureStore.read(
key: testWalletId + "_receiveDerivationsP2WPKH");
await secureStore?.write(
await secureStore.write(
key: testWalletId + "_receiveDerivationsP2WPKH",
value: rcv84?.replaceFirst(
"bc1qggtj4ka8jsaj44hhd5mpamx7mp34m2d3w7k0m0",
@ -1436,10 +1436,10 @@ void main() {
true);
}
expect(secureStore?.interactions, 20);
expect(secureStore?.writes, 10);
expect(secureStore?.reads, 10);
expect(secureStore?.deletes, 0);
expect(secureStore.interactions, 20);
expect(secureStore.writes, 10);
expect(secureStore.reads, 10);
expect(secureStore.deletes, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -1456,7 +1456,7 @@ void main() {
expect(didThrow, true);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -1472,7 +1472,7 @@ void main() {
expect(didThrow, true);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -1492,7 +1492,7 @@ void main() {
rawTx: "a string", requestID: anyNamed("requestID")))
.called(1);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -1513,7 +1513,7 @@ void main() {
rawTx: "a string", requestID: anyNamed("requestID")))
.called(1);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(priceAPI);
@ -1538,7 +1538,7 @@ void main() {
rawTx: "a string", requestID: anyNamed("requestID")))
.called(1);
expect(secureStore?.interactions, 0);
expect(secureStore.interactions, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
@ -1658,10 +1658,10 @@ void main() {
true);
}
expect(secureStore?.interactions, 14);
expect(secureStore?.writes, 7);
expect(secureStore?.reads, 7);
expect(secureStore?.deletes, 0);
expect(secureStore.interactions, 14);
expect(secureStore.writes, 7);
expect(secureStore.reads, 7);
expect(secureStore.deletes, 0);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -1726,10 +1726,10 @@ void main() {
verify(client?.getBatchHistory(args: map)).called(1);
}
expect(secureStore?.interactions, 14);
expect(secureStore?.writes, 7);
expect(secureStore?.reads, 7);
expect(secureStore?.deletes, 0);
expect(secureStore.interactions, 14);
expect(secureStore.writes, 7);
expect(secureStore.reads, 7);
expect(secureStore.deletes, 0);
// verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);

View file

@ -465,6 +465,22 @@ class MockCachedElectrumX extends _i1.Mock implements _i7.CachedElectrumX {
_i6.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
) as _i6.Future<Map<String, dynamic>>);
@override
String base64ToHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToHex,
[source],
),
returnValue: '',
) as String);
@override
String base64ToReverseHex(String? source) => (super.noSuchMethod(
Invocation.method(
#base64ToReverseHex,
[source],
),
returnValue: '',
) as String);
@override
_i6.Future<Map<String, dynamic>> getTransaction({
required String? txHash,
required _i8.Coin? coin,

View file

@ -1,5 +1,4 @@
import 'dart:core';
import 'dart:core' as core;
import 'dart:io';
import 'dart:math';
@ -19,14 +18,12 @@ import 'package:hive/hive.dart';
import 'package:hive_test/hive_test.dart';
import 'package:mockito/annotations.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'wownero_wallet_test_data.dart';
FakeSecureStorage? storage;
WalletService? walletService;
SharedPreferences? prefs;
KeyService? keysStorage;
WowneroWalletBase? walletBase;
late WalletCreationService _walletCreationService;
@ -41,7 +38,6 @@ WalletType type = WalletType.wownero;
@GenerateMocks([])
void main() async {
storage = FakeSecureStorage();
prefs = await SharedPreferences.getInstance();
keysStorage = KeyService(storage!);
WalletInfo walletInfo = WalletInfo.external(
id: '',
@ -102,7 +98,6 @@ void main() async {
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
walletService: walletService,
keyService: keysStorage,
);
@ -127,19 +122,24 @@ void main() async {
walletBase = wallet as WowneroWalletBase;
expect(
await walletBase!.validateAddress(wallet.walletAddresses.address ?? ''), true);
await walletBase!
.validateAddress(wallet.walletAddresses.address ?? ''),
true);
} catch (_) {
hasThrown = true;
}
expect(hasThrown, false);
// Address validation
expect(await walletBase!.validateAddress(''), false);
expect(
await walletBase!.validateAddress(''), false);
await walletBase!.validateAddress(
'Wo3jmHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmsEi'),
true);
expect(
await walletBase!.validateAddress('Wo3jmHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmsEi'), true);
expect(
await walletBase!.validateAddress('WasdfHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmjkl'), false);
await walletBase!.validateAddress(
'WasdfHvTMLwE6h29fpgcb8PbJSpaKuqM7XTXVfiiu8bLCZsJvrQCbQSJR48Vo3BWNQKsMsXZ4VixndXTH25QtorC27NCjmjkl'),
false);
walletBase?.close();
walletBase = wallet as WowneroWalletBase;
@ -174,7 +174,6 @@ void main() async {
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
walletService: walletService,
keyService: keysStorage,
);
@ -248,7 +247,6 @@ void main() async {
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
walletService: walletService,
keyService: keysStorage,
);
@ -312,7 +310,6 @@ void main() async {
_walletCreationService = WalletCreationService(
secureStorage: storage,
sharedPreferences: prefs,
walletService: walletService,
keyService: keysStorage,
);
@ -367,6 +364,6 @@ Future<String> pathForWalletDir(
}
Future<String> pathForWallet(
{required String name, required WalletType type}) async =>
{required String name, required WalletType type}) async =>
await pathForWalletDir(name: name, type: type)
.then((path) => path + '/$name');

View file

@ -141,7 +141,8 @@ void main() {
);
setUp(() async {
await NodeService().updateDefaults();
await NodeService(secureStorageInterface: FakeSecureStorage())
.updateDefaults();
});
test("setPrimaryNodeFor and getPrimaryNodeFor", () async {

View file

@ -32,7 +32,7 @@ void main() {
});
test("get walletNames", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect((await service.walletNames).toString(),
'{wallet_id: WalletInfo: {"name":"My Firo Wallet","id":"wallet_id","coin":"bitcoin"}, wallet_id2: WalletInfo: {"name":"wallet2","id":"wallet_id2","coin":"bitcoin"}}');
});
@ -40,13 +40,13 @@ void main() {
test("get null wallet names", () async {
final wallets = await Hive.openBox<dynamic>('wallets');
await wallets.put('names', null);
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(await service.walletNames, <String, WalletInfo>{});
expect((await service.walletNames).toString(), '{}');
});
test("rename wallet to same name", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(
await service.renameWallet(
from: "My Firo Wallet",
@ -58,7 +58,7 @@ void main() {
});
test("rename wallet to new name", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(
await service.renameWallet(
from: "My Firo Wallet",
@ -71,7 +71,7 @@ void main() {
});
test("attempt rename wallet to another existing name", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(
await service.renameWallet(
from: "My Firo Wallet",
@ -83,7 +83,7 @@ void main() {
});
test("add new wallet name", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(
await service.addNewWallet(
name: "wallet3", coin: Coin.bitcoin, shouldNotifyListeners: false),
@ -92,7 +92,7 @@ void main() {
});
test("add duplicate wallet name fails", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(
await service.addNewWallet(
name: "wallet2", coin: Coin.bitcoin, shouldNotifyListeners: false),
@ -103,27 +103,27 @@ void main() {
test("check for duplicates when null names", () async {
final wallets = await Hive.openBox<dynamic>('wallets');
await wallets.put('names', null);
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(await service.checkForDuplicate("anything"), false);
});
test("check for duplicates when some names with no matches", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(await service.checkForDuplicate("anything"), false);
});
test("check for duplicates when some names with a match", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(await service.checkForDuplicate("wallet2"), true);
});
test("get existing wallet id", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expect(await service.getWalletId("wallet2"), "wallet_id2");
});
test("get non existent wallet id", () async {
final service = WalletsService();
final service = WalletsService(secureStorageInterface: FakeSecureStorage());
expectLater(await service.getWalletId("wallet 99"), null);
});

View file

@ -3,12 +3,12 @@
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i4;
import 'dart:async' as _i3;
import 'package:flutter_secure_storage/flutter_secure_storage.dart' as _i2;
import 'package:flutter_secure_storage/flutter_secure_storage.dart' as _i4;
import 'package:mockito/mockito.dart' as _i1;
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
as _i3;
as _i2;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
@ -21,43 +21,24 @@ import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart'
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
class _FakeFlutterSecureStorage_0 extends _i1.SmartFake
implements _i2.FlutterSecureStorage {
_FakeFlutterSecureStorage_0(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
/// A class which mocks [SecureStorageWrapper].
///
/// See the documentation for Mockito's code generation for more information.
class MockSecureStorageWrapper extends _i1.Mock
implements _i3.SecureStorageWrapper {
implements _i2.SecureStorageWrapper {
MockSecureStorageWrapper() {
_i1.throwOnMissingStub(this);
}
@override
_i2.FlutterSecureStorage get secureStore => (super.noSuchMethod(
Invocation.getter(#secureStore),
returnValue: _FakeFlutterSecureStorage_0(
this,
Invocation.getter(#secureStore),
),
) as _i2.FlutterSecureStorage);
@override
_i4.Future<String?> read({
_i3.Future<String?> read({
required String? key,
_i2.IOSOptions? iOptions,
_i2.AndroidOptions? aOptions,
_i2.LinuxOptions? lOptions,
_i2.WebOptions? webOptions,
_i2.MacOsOptions? mOptions,
_i2.WindowsOptions? wOptions,
_i4.IOSOptions? iOptions,
_i4.AndroidOptions? aOptions,
_i4.LinuxOptions? lOptions,
_i4.WebOptions? webOptions,
_i4.MacOsOptions? mOptions,
_i4.WindowsOptions? wOptions,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -73,18 +54,18 @@ class MockSecureStorageWrapper extends _i1.Mock
#wOptions: wOptions,
},
),
returnValue: _i4.Future<String?>.value(),
) as _i4.Future<String?>);
returnValue: _i3.Future<String?>.value(),
) as _i3.Future<String?>);
@override
_i4.Future<void> write({
_i3.Future<void> write({
required String? key,
required String? value,
_i2.IOSOptions? iOptions,
_i2.AndroidOptions? aOptions,
_i2.LinuxOptions? lOptions,
_i2.WebOptions? webOptions,
_i2.MacOsOptions? mOptions,
_i2.WindowsOptions? wOptions,
_i4.IOSOptions? iOptions,
_i4.AndroidOptions? aOptions,
_i4.LinuxOptions? lOptions,
_i4.WebOptions? webOptions,
_i4.MacOsOptions? mOptions,
_i4.WindowsOptions? wOptions,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -101,18 +82,18 @@ class MockSecureStorageWrapper extends _i1.Mock
#wOptions: wOptions,
},
),
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
@override
_i4.Future<void> delete({
_i3.Future<void> delete({
required String? key,
_i2.IOSOptions? iOptions,
_i2.AndroidOptions? aOptions,
_i2.LinuxOptions? lOptions,
_i2.WebOptions? webOptions,
_i2.MacOsOptions? mOptions,
_i2.WindowsOptions? wOptions,
_i4.IOSOptions? iOptions,
_i4.AndroidOptions? aOptions,
_i4.LinuxOptions? lOptions,
_i4.WebOptions? webOptions,
_i4.MacOsOptions? mOptions,
_i4.WindowsOptions? wOptions,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -128,7 +109,7 @@ class MockSecureStorageWrapper extends _i1.Mock
#wOptions: wOptions,
},
),
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);
returnValue: _i3.Future<void>.value(),
returnValueForMissingStub: _i3.Future<void>.value(),
) as _i3.Future<void>);
}

View file

@ -166,7 +166,7 @@ class _FakeElectrumXNode_11 extends _i1.SmartFake
}
class _FakeFlutterSecureStorageInterface_12 extends _i1.SmartFake
implements _i12.FlutterSecureStorageInterface {
implements _i12.SecureStorageInterface {
_FakeFlutterSecureStorageInterface_12(
Object parent,
Invocation parentInvocation,
@ -681,6 +681,14 @@ class MockBitcoinWallet extends _i1.Mock implements _i19.BitcoinWallet {
returnValueForMissingStub: null,
);
@override
set cachedTxData(_i8.TransactionData? _cachedTxData) => super.noSuchMethod(
Invocation.setter(
#cachedTxData,
_cachedTxData,
),
returnValueForMissingStub: null,
);
@override
bool get isActive => (super.noSuchMethod(
Invocation.getter(#isActive),
returnValue: false,
@ -1063,6 +1071,16 @@ class MockBitcoinWallet extends _i1.Mock implements _i19.BitcoinWallet {
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
@override
_i16.Future<void> updateSentCachedTxData(Map<String, dynamic>? txData) =>
(super.noSuchMethod(
Invocation.method(
#updateSentCachedTxData,
[txData],
),
returnValue: _i16.Future<void>.value(),
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
@override
bool validateAddress(String? address) => (super.noSuchMethod(
Invocation.method(
#validateAddress,
@ -1380,14 +1398,13 @@ class MockLocaleService extends _i1.Mock implements _i20.LocaleService {
/// See the documentation for Mockito's code generation for more information.
class MockNodeService extends _i1.Mock implements _i3.NodeService {
@override
_i12.FlutterSecureStorageInterface get secureStorageInterface =>
(super.noSuchMethod(
_i12.SecureStorageInterface get secureStorageInterface => (super.noSuchMethod(
Invocation.getter(#secureStorageInterface),
returnValue: _FakeFlutterSecureStorageInterface_12(
this,
Invocation.getter(#secureStorageInterface),
),
) as _i12.FlutterSecureStorageInterface);
) as _i12.SecureStorageInterface);
@override
List<_i21.NodeModel> get primaryNodes => (super.noSuchMethod(
Invocation.getter(#primaryNodes),
@ -2291,4 +2308,14 @@ class MockCoinServiceAPI extends _i1.Mock implements _i13.CoinServiceAPI {
),
returnValue: _i16.Future<bool>.value(false),
) as _i16.Future<bool>);
@override
_i16.Future<void> updateSentCachedTxData(Map<String, dynamic>? txData) =>
(super.noSuchMethod(
Invocation.method(
#updateSentCachedTxData,
[txData],
),
returnValue: _i16.Future<void>.value(),
returnValueForMissingStub: _i16.Future<void>.value(),
) as _i16.Future<void>);
}

Some files were not shown because too many files have changed in this diff Show more