Skip to content
1 change: 1 addition & 0 deletions assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
"customSeedWarningText": "Custom seed phrases are generally less secure and easier to crack than a generated BIP39-compliant seed phrase. To confirm you understand and are aware of the risk, type \"I understand\" in the box below.",
"customSeedIUnderstand": "i understand",
"walletCreationBip39SeedError": "BIP39 seed validation failed, try again or select 'Allow custom seed'",
"walletCreationHdBip39SeedError": "Your input seed is not BIP39 compliant, and can not be used in multi-address wallet mode. Please try again, or disable multi-address wallet mode and select 'Allow custom seed' to proceed.",
"walletPageNoSuchAsset": "No assets match search criteria",
"swapCoin": "Swap",
"fiatBalance": "Fiat balance",
Expand Down
146 changes: 73 additions & 73 deletions lib/bloc/auth_bloc/auth_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
AuthBloc(this._kdfSdk, this._walletsRepository)
: super(AuthBlocState.initial()) {
on<AuthModeChanged>(_onAuthChanged);
on<AuthStateClearRequested>(_onClearState);
on<AuthSignOutRequested>(_onLogout);
on<AuthSignInRequested>(_onLogIn);
on<AuthRegisterRequested>(_onRegister);
Expand All @@ -31,29 +32,23 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {

final KomodoDefiSdk _kdfSdk;
final WalletsRepository _walletsRepository;
StreamSubscription<KdfUser?>? _authorizationSubscription;
StreamSubscription<KdfUser?>? _authChangesSubscription;

@override
Future<void> close() async {
await _authorizationSubscription?.cancel();
await _authChangesSubscription?.cancel();
await super.close();
}

Future<void> _onLogout(
AuthSignOutRequested event,
Emitter<AuthBlocState> emit,
) async {
log(
'Logging out from a wallet',
path: 'auth_bloc => _logOut',
).ignore();

log('Logging out from a wallet', path: 'auth_bloc => _logOut').ignore();
emit(AuthBlocState.loading());
await _kdfSdk.auth.signOut();
log(
'Logged out from a wallet',
path: 'auth_bloc => _logOut',
).ignore();
emit(const AuthBlocState(mode: AuthorizeMode.noLogin, currentUser: null));
await _authChangesSubscription?.cancel();
emit(AuthBlocState.initial());
}

Future<void> _onLogIn(
Expand All @@ -72,6 +67,7 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
}

log('login from a wallet', path: 'auth_bloc => _reLogin').ignore();
emit(AuthBlocState.loading());
await _kdfSdk.auth.signIn(
walletName: event.wallet.name,
password: event.password,
Expand All @@ -81,22 +77,20 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
: DerivationMethod.iguana,
),
);
final KdfUser? currentUser = await _kdfSdk.auth.currentUser;
if (currentUser == null) {
return emit(AuthBlocState.error('Failed to login'));
}

log('logged in from a wallet', path: 'auth_bloc => _reLogin').ignore();
emit(
AuthBlocState(
mode: AuthorizeMode.logIn,
currentUser: await _kdfSdk.auth.currentUser,
),
);
emit(AuthBlocState.loggedIn(currentUser));
_listenToAuthStateChanges();
} catch (e, s) {
log(
'Failed to login wallet ${event.wallet.name}',
isError: true,
trace: s,
path: 'auth_bloc -> onLogin',
).ignore();
emit(const AuthBlocState(mode: AuthorizeMode.noLogin));
final error = 'Failed to login wallet ${event.wallet.name}';
log(error, isError: true, trace: s, path: 'auth_bloc -> onLogin')
.ignore();
emit(AuthBlocState.error(error));
await _authChangesSubscription?.cancel();
}
}

Expand All @@ -107,20 +101,21 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
emit(AuthBlocState(mode: event.mode, currentUser: event.currentUser));
}

Future<void> _onClearState(
AuthStateClearRequested event,
Emitter<AuthBlocState> emit,
) async {
await _authChangesSubscription?.cancel();
emit(AuthBlocState.initial());
}

Future<void> _onRegister(
AuthRegisterRequested event,
Emitter<AuthBlocState> emit,
) async {
try {
final existingWallets = await _kdfSdk.auth.getUsers();
final walletExists = existingWallets
.any((KdfUser user) => user.walletId.name == event.wallet.name);
if (walletExists) {
add(
AuthSignInRequested(wallet: event.wallet, password: event.password),
);
log('Wallet ${event.wallet.name} already exist, attempting sign-in')
.ignore();
emit(AuthBlocState.loading());
if (await _didSignInExistingWallet(event.wallet, event.password)) {
return;
}

Expand All @@ -134,27 +129,26 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
: DerivationMethod.iguana,
),
);

if (!await _kdfSdk.auth.isSignedIn()) {
throw Exception('Registration failed: user is not signed in');
}

log('registered from a wallet', path: 'auth_bloc => _register').ignore();
await _kdfSdk.setWalletType(event.wallet.config.type);
await _kdfSdk.confirmSeedBackup(hasBackup: false);
emit(
AuthBlocState(
mode: AuthorizeMode.logIn,
currentUser: await _kdfSdk.auth.currentUser,
),
);
final currentUser = await _kdfSdk.auth.currentUser;
if (currentUser == null) {
throw Exception('Registration failed: user is not signed in');
}
emit(AuthBlocState.loggedIn(currentUser));
_listenToAuthStateChanges();
} catch (e, s) {
log(
'Failed to register wallet ${event.wallet.name}',
isError: true,
trace: s,
path: 'auth_bloc -> onRegister',
).ignore();
emit(const AuthBlocState(mode: AuthorizeMode.noLogin));
final error = 'Failed to register wallet ${event.wallet.name}';
log(error, isError: true, trace: s, path: 'auth_bloc -> onRegister')
.ignore();
emit(AuthBlocState.error(error));
await _authChangesSubscription?.cancel();
}
}

Expand All @@ -163,15 +157,8 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
Emitter<AuthBlocState> emit,
) async {
try {
final existingWallets = await _kdfSdk.auth.getUsers();
final walletExists = existingWallets
.any((KdfUser user) => user.walletId.name == event.wallet.name);
if (walletExists) {
add(
AuthSignInRequested(wallet: event.wallet, password: event.password),
);
log('Wallet ${event.wallet.name} already exist, attempting sign-in')
.ignore();
emit(AuthBlocState.loading());
if (await _didSignInExistingWallet(event.wallet, event.password)) {
return;
}

Expand All @@ -186,20 +173,19 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
: DerivationMethod.iguana,
),
);

if (!await _kdfSdk.auth.isSignedIn()) {
throw Exception('Registration failed: user is not signed in');
}
log('restored from a wallet', path: 'auth_bloc => _restore').ignore();

log('restored from a wallet', path: 'auth_bloc => _restore').ignore();
await _kdfSdk.setWalletType(event.wallet.config.type);
await _kdfSdk.confirmSeedBackup(hasBackup: event.wallet.config.hasBackup);

emit(
AuthBlocState(
mode: AuthorizeMode.logIn,
currentUser: await _kdfSdk.auth.currentUser,
),
);
final currentUser = await _kdfSdk.auth.currentUser;
if (currentUser == null) {
throw Exception('Registration failed: user is not signed in');
}
emit(AuthBlocState.loggedIn(currentUser));

// Delete legacy wallet on successful restoration & login to avoid
// duplicates in the wallet list
Expand All @@ -210,16 +196,30 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {

_listenToAuthStateChanges();
} catch (e, s) {
log(
'Failed to restore existing wallet ${event.wallet.name}',
isError: true,
trace: s,
path: 'auth_bloc -> onRestore',
).ignore();
emit(const AuthBlocState(mode: AuthorizeMode.noLogin));
final error = 'Failed to restore existing wallet ${event.wallet.name}';
log(error, isError: true, trace: s, path: 'auth_bloc -> onRestore')
.ignore();
emit(AuthBlocState.error(error));
await _authChangesSubscription?.cancel();
}
}

Future<bool> _didSignInExistingWallet(
Wallet wallet,
String password,
) async {
final existingWallets = await _kdfSdk.auth.getUsers();
final walletExists = existingWallets
.any((KdfUser user) => user.walletId.name == wallet.name);
if (walletExists) {
add(AuthSignInRequested(wallet: wallet, password: password));
log('Wallet ${wallet.name} already exist, attempting sign-in').ignore();
return true;
}

return false;
}

Future<void> _onSeedBackupConfirmed(
AuthSeedBackupConfirmed event,
Emitter<AuthBlocState> emit,
Expand Down Expand Up @@ -254,8 +254,8 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> {
}

void _listenToAuthStateChanges() {
_authorizationSubscription?.cancel();
_authorizationSubscription = _kdfSdk.auth.authStateChanges.listen((user) {
_authChangesSubscription?.cancel();
_authChangesSubscription = _kdfSdk.auth.authStateChanges.listen((user) {
final AuthorizeMode event =
user != null ? AuthorizeMode.logIn : AuthorizeMode.noLogin;
add(AuthModeChanged(mode: event, currentUser: user));
Expand Down
4 changes: 4 additions & 0 deletions lib/bloc/auth_bloc/auth_bloc_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ class AuthModeChanged extends AuthBlocEvent {
final KdfUser? currentUser;
}

class AuthStateClearRequested extends AuthBlocEvent {
const AuthStateClearRequested();
}

class AuthSignOutRequested extends AuthBlocEvent {
const AuthSignOutRequested();
}
Expand Down
29 changes: 27 additions & 2 deletions lib/bloc/auth_bloc/auth_bloc_state.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,41 @@
part of 'auth_bloc.dart';

enum AuthStatus { initial, loading, success, failure }

class AuthBlocState extends Equatable {
const AuthBlocState({required this.mode, this.currentUser});
const AuthBlocState({
required this.mode,
this.currentUser,
this.status = AuthStatus.initial,
this.errorMessage,
});

factory AuthBlocState.initial() =>
const AuthBlocState(mode: AuthorizeMode.noLogin);
factory AuthBlocState.loading() => const AuthBlocState(
mode: AuthorizeMode.noLogin,
status: AuthStatus.loading,
);
factory AuthBlocState.error(String errorMessage) => AuthBlocState(
mode: AuthorizeMode.noLogin,
status: AuthStatus.failure,
errorMessage: errorMessage,
);
factory AuthBlocState.loggedIn(KdfUser user) => AuthBlocState(
mode: AuthorizeMode.logIn,
status: AuthStatus.success,
currentUser: user,
);

final KdfUser? currentUser;
final AuthorizeMode mode;
final AuthStatus status;
final String? errorMessage;

bool get isSignedIn => currentUser != null;
bool get isLoading => status == AuthStatus.loading;
bool get isError => status == AuthStatus.failure;

@override
List<Object?> get props => [mode, currentUser];
List<Object?> get props => [mode, currentUser, status, errorMessage];
}
5 changes: 4 additions & 1 deletion lib/generated/codegen_loader.g.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart

// ignore_for_file: constant_identifier_names

abstract class LocaleKeys {
static const plsActivateKmd = 'plsActivateKmd';
static const rewardClaiming = 'rewardClaiming';
Expand Down Expand Up @@ -196,6 +198,7 @@ abstract class LocaleKeys {
static const customSeedWarningText = 'customSeedWarningText';
static const customSeedIUnderstand = 'customSeedIUnderstand';
static const walletCreationBip39SeedError = 'walletCreationBip39SeedError';
static const walletCreationHdBip39SeedError = 'walletCreationHdBip39SeedError';
static const walletPageNoSuchAsset = 'walletPageNoSuchAsset';
static const swapCoin = 'swapCoin';
static const fiatBalance = 'fiatBalance';
Expand Down Expand Up @@ -606,8 +609,8 @@ abstract class LocaleKeys {
static const importTokenWarning = 'importTokenWarning';
static const importToken = 'importToken';
static const selectNetwork = 'selectNetwork';
static const tokenNotFound = 'tokenNotFound';
static const tokenContractAddress = 'tokenContractAddress';
static const tokenNotFound = 'tokenNotFound';
static const decimals = 'decimals';
static const onlySendToThisAddress = 'onlySendToThisAddress';
static const scanTheQrCode = 'scanTheQrCode';
Expand Down
Loading