diff --git a/lib/app_shell.dart b/lib/app_shell.dart index 813c167871..dc80c88e0e 100644 --- a/lib/app_shell.dart +++ b/lib/app_shell.dart @@ -2,6 +2,7 @@ import 'package:ardrive/components/profile_card.dart'; import 'package:ardrive/components/side_bar.dart'; import 'package:ardrive/pages/drive_detail/components/hover_widget.dart'; import 'package:ardrive/utils/html/html_util.dart'; +import 'package:ardrive/utils/logger/logger.dart'; import 'package:ardrive/utils/size_constants.dart'; import 'package:ardrive_ui/ardrive_ui.dart'; import 'package:flutter/material.dart'; @@ -32,16 +33,26 @@ class AppShellState extends State { bool _showWalletSwitchDialog = true; @override Widget build(BuildContext context) => BlocBuilder( - builder: (context, state) { + builder: (context, _) { onArConnectWalletSwitch(() { - if (_showWalletSwitchDialog) { - showDialog( - context: context, - builder: (context) => const WalletSwitchDialog(), - ); - } - //Used to prevent the dialog being shown multiple times. - _showWalletSwitchDialog = false; + context + .read() + .isCurrentProfileArConnect() + .then((isCurrentProfileArConnect) { + if (_showWalletSwitchDialog) { + if (isCurrentProfileArConnect) { + showDialog( + context: context, + builder: (context) => const WalletSwitchDialog(), + ); + } else { + logger.d('Wallet switch detected while not logged in' + ' to ArConnect. Ignoring.'); + } + } + //Used to prevent the dialog being shown multiple times. + _showWalletSwitchDialog = false; + }); }); Widget buildPage(scaffold) => Material( diff --git a/lib/authentication/ardrive_auth.dart b/lib/authentication/ardrive_auth.dart index f1fb6d1837..1a9b45cd13 100644 --- a/lib/authentication/ardrive_auth.dart +++ b/lib/authentication/ardrive_auth.dart @@ -26,7 +26,7 @@ abstract class ArDriveAuth { Future unlockWithBiometrics({required String localizedReason}); Future unlockUser({required String password}); Future logout(); - User? get currentUser; + User get currentUser; Stream onAuthStateChanged(); Future isBiometricsEnabled(); @@ -216,25 +216,15 @@ class ArDriveAuthImpl implements ArDriveAuth { try { if (_currentUser != null) { - if (currentUser.profileType == ProfileType.arConnect) { - try { - await _arConnectService.disconnect(); - } catch (e) { - logger.e('Failed to disconnect from ArConnect', e); - } - } - - _secureKeyValueStore.remove('password'); - _secureKeyValueStore.remove('biometricEnabled'); - + await _secureKeyValueStore.remove('password'); + await _secureKeyValueStore.remove('biometricEnabled'); currentUser = null; firstPrivateDriveTxId = null; - + await _disconnectFromArConnect(); _userStreamController.add(null); } await _databaseHelpers.deleteAllTables(); - (await _metadataCache).clear(); } catch (e) { logger.e('Failed to logout user', e); @@ -242,6 +232,17 @@ class ArDriveAuthImpl implements ArDriveAuth { } } + Future _disconnectFromArConnect() async { + final hasArConnectPermissions = await _arConnectService.checkPermissions(); + if (hasArConnectPermissions) { + try { + await _arConnectService.disconnect(); + } catch (e) { + logger.e('Failed to disconnect from ArConnect', e); + } + } + } + @override Future isBiometricsEnabled() { return _biometricAuthentication.isEnabled(); diff --git a/lib/authentication/login/blocs/login_bloc.dart b/lib/authentication/login/blocs/login_bloc.dart index cdd2f90653..1df9402f70 100644 --- a/lib/authentication/login/blocs/login_bloc.dart +++ b/lib/authentication/login/blocs/login_bloc.dart @@ -77,7 +77,9 @@ class LoginBloc extends Bloc { } Future _handleUnlockUserWithBiometricsEvent( - UnLockWithBiometrics event, Emitter emit) async { + UnLockWithBiometrics event, + Emitter emit, + ) async { final previousState = state; try { @@ -107,7 +109,9 @@ class LoginBloc extends Bloc { } Future _handleAddWalletFileEvent( - AddWalletFile event, Emitter emit) async { + AddWalletFile event, + Emitter emit, + ) async { final previousState = state; try { @@ -130,7 +134,9 @@ class LoginBloc extends Bloc { } Future _handleLoginWithPasswordEvent( - LoginWithPassword event, Emitter emit) async { + LoginWithPassword event, + Emitter emit, + ) async { final previousState = state; try { @@ -150,7 +156,9 @@ class LoginBloc extends Bloc { } Future _handleCheckIfUserIsLoggedInEvent( - CheckIfUserIsLoggedIn event, Emitter emit) async { + CheckIfUserIsLoggedIn event, + Emitter emit, + ) async { emit(LoginLoading()); if (await _arDriveAuth.isUserLoggedIn()) { @@ -172,7 +180,9 @@ class LoginBloc extends Bloc { } Future _handleUnlockUserWithPasswordEvent( - UnlockUserWithPassword event, Emitter emit) async { + UnlockUserWithPassword event, + Emitter emit, + ) async { final previousState = state; emit(LoginLoading()); @@ -192,7 +202,9 @@ class LoginBloc extends Bloc { } Future _handleCreatePasswordEvent( - CreatePassword event, Emitter emit) async { + CreatePassword event, + Emitter emit, + ) async { final previousState = state; emit(LoginLoading()); @@ -212,7 +224,9 @@ class LoginBloc extends Bloc { } Future _handleAddWalletFromArConnectEvent( - AddWalletFromArConnect event, Emitter emit) async { + AddWalletFromArConnect event, + Emitter emit, + ) async { final previousState = state; try { @@ -260,10 +274,6 @@ class LoginBloc extends Bloc { await _arDriveAuth.logout(); } - if (_isArConnectWallet()) { - await _arConnectService.disconnect(); - } - emit(LoginInitial(_arConnectService.isExtensionPresent())); } @@ -312,7 +322,13 @@ class LoginBloc extends Bloc { return; } - onArConnectWalletSwitch(() { + onArConnectWalletSwitch(() async { + final isUserLoggedIng = await _arDriveAuth.isUserLoggedIn(); + if (isUserLoggedIng && !_isArConnectWallet()) { + logger.d('Wallet switch detected. Is current profile ArConnect: false'); + return; + } + if (ignoreNextWaletSwitch) { ignoreNextWaletSwitch = false; return; @@ -322,7 +338,7 @@ class LoginBloc extends Bloc { // ignore: invalid_use_of_visible_for_testing_member emit(const LoginFailure(WalletMismatchException())); - _arDriveAuth.logout(); + await _arDriveAuth.logout(); // ignore: invalid_use_of_visible_for_testing_member emit(const LoginInitial(true)); @@ -343,7 +359,9 @@ class LoginBloc extends Bloc { } Future _handleEnterSeedPhrase( - EnterSeedPhrase event, Emitter emit) async { + EnterSeedPhrase event, + Emitter emit, + ) async { emit(LoginEnterSeedPhrase()); } @@ -358,7 +376,9 @@ class LoginBloc extends Bloc { } Future _handleAddWalletFromCompleterEvent( - AddWalletFromCompleter event, Emitter emit) async { + AddWalletFromCompleter event, + Emitter emit, + ) async { profileType = ProfileType.json; Completer completer = event.walletCompleter; @@ -380,14 +400,18 @@ class LoginBloc extends Bloc { } Future _handleCreateNewWalletEvent( - CreateNewWallet event, Emitter emit) async { + CreateNewWallet event, + Emitter emit, + ) async { profileType = ProfileType.json; final mnemonic = bip39.generateMnemonic(); emit(LoginCreateNewWallet(mnemonic)); } Future _handleCompleteWalletGenerationEvent( - CompleteWalletGeneration event, Emitter emit) async { + CompleteWalletGeneration event, + Emitter emit, + ) async { final previousState = state; final wallet = event.wallet; diff --git a/lib/blocs/profile_add/profile_add_cubit.dart b/lib/blocs/profile_add/profile_add_cubit.dart index 5dc8b47cb1..eec59b71e3 100644 --- a/lib/blocs/profile_add/profile_add_cubit.dart +++ b/lib/blocs/profile_add/profile_add_cubit.dart @@ -52,7 +52,7 @@ class ProfileAddCubit extends Cubit { return arconnect.isExtensionPresent(); } - ProfileType? getProfileType() => _profileType; + ProfileType getProfileType() => _profileType; Future promptForWallet() async { if (_profileType == ProfileType.arConnect) { diff --git a/lib/blocs/upload/upload_cubit.dart b/lib/blocs/upload/upload_cubit.dart index eae65f7968..006d8d0817 100644 --- a/lib/blocs/upload/upload_cubit.dart +++ b/lib/blocs/upload/upload_cubit.dart @@ -321,7 +321,7 @@ class UploadCubit extends Cubit { try { final uploadPreparation = await _arDriveUploadManager.prepareUpload( params: UploadParams( - user: _auth.currentUser!, + user: _auth.currentUser, files: files, targetFolder: _targetFolder, targetDrive: _targetDrive, @@ -351,7 +351,7 @@ class UploadCubit extends Cubit { 'UploadPlan For AR: ${uploadPreparation.uploadPaymentInfo.arCostEstimate.toString()}\n' 'UploadPlan For Turbo: ${uploadPreparation.uploadPlansPreparation.uploadPlanForTurbo.toString()}\n' 'Turbo Balance: ${uploadPreparation.uploadPaymentInfo.turboBalance}\n' - 'AR Balance: ${_auth.currentUser!.walletBalance}\n' + 'AR Balance: ${_auth.currentUser.walletBalance}\n' 'Is Turbo Upload Possible: ${paymentInfo.isUploadEligibleToTurbo}\n' 'Is Zero Balance: $isTurboZeroBalance\n', ); @@ -396,7 +396,7 @@ class UploadCubit extends Cubit { costEstimateTurbo: paymentInfo.turboCostEstimate, credits: literalBalance, arBalance: - convertCreditsToLiteralString(_auth.currentUser!.walletBalance), + convertCreditsToLiteralString(_auth.currentUser.walletBalance), uploadIsPublic: _targetDrive.isPublic, sufficientArBalance: profile.walletBalance >= paymentInfo.arCostEstimate.totalCost, @@ -530,7 +530,7 @@ class UploadCubit extends Cubit { } ArDriveUploader _getUploader() { - final wallet = _auth.currentUser!.wallet; + final wallet = _auth.currentUser.wallet; final turboUploader = TurboUploader(_turbo, wallet); final arweaveUploader = ArweaveBundleUploader(_arweave.client); @@ -558,7 +558,7 @@ class UploadCubit extends Cubit { arweaveService: _arweave, turboUploadService: _turbo, pstService: _pst, - wallet: _auth.currentUser!.wallet, + wallet: _auth.currentUser.wallet, isArConnect: await _profileCubit.isCurrentProfileArConnect(), useTurbo: _uploadMethod == UploadMethod.turbo, ); diff --git a/lib/components/upload_form.dart b/lib/components/upload_form.dart index 5c9bf504a3..ded7d5aaf7 100644 --- a/lib/components/upload_form.dart +++ b/lib/components/upload_form.dart @@ -81,7 +81,7 @@ Future promptToUpload( ), turboUploadCostCalculator: TurboUploadCostCalculator( priceEstimator: TurboPriceEstimator( - wallet: context.read().currentUser!.wallet, + wallet: context.read().currentUser.wallet, costCalculator: TurboCostCalculator( paymentService: context.read(), ), diff --git a/lib/core/upload/uploader.dart b/lib/core/upload/uploader.dart index c319edf7a1..94cd53eae3 100644 --- a/lib/core/upload/uploader.dart +++ b/lib/core/upload/uploader.dart @@ -357,7 +357,7 @@ class UploadPaymentEvaluator { /// If we can't get the balance, turbo won't be available try { turboBalance = - await _turboBalanceRetriever.getBalance(_auth.currentUser!.wallet); + await _turboBalanceRetriever.getBalance(_auth.currentUser.wallet); logger.i('Turbo balance: $turboBalance'); } catch (e, stacktrace) { diff --git a/lib/pages/drive_detail/components/drive_file_drop_zone.dart b/lib/pages/drive_detail/components/drive_file_drop_zone.dart index 9578f6e9fd..9df36f46f2 100644 --- a/lib/pages/drive_detail/components/drive_file_drop_zone.dart +++ b/lib/pages/drive_detail/components/drive_file_drop_zone.dart @@ -129,7 +129,7 @@ class DriveFileDropZoneState extends State { ), turboUploadCostCalculator: TurboUploadCostCalculator( priceEstimator: TurboPriceEstimator( - wallet: context.read().currentUser!.wallet, + wallet: context.read().currentUser.wallet, costCalculator: TurboCostCalculator( paymentService: context.read(), ), diff --git a/lib/turbo/topup/views/topup_modal.dart b/lib/turbo/topup/views/topup_modal.dart index 6c656283cd..832af999b3 100644 --- a/lib/turbo/topup/views/topup_modal.dart +++ b/lib/turbo/topup/views/topup_modal.dart @@ -33,7 +33,7 @@ void showTurboModal(BuildContext context, {Function()? onSuccess}) { ); final priceEstimator = TurboPriceEstimator( - wallet: context.read().currentUser!.wallet, + wallet: context.read().currentUser.wallet, paymentService: context.read(), costCalculator: costCalculator, ); @@ -52,7 +52,7 @@ void showTurboModal(BuildContext context, {Function()? onSuccess}) { balanceRetriever: balanceRetriever, priceEstimator: priceEstimator, paymentProvider: turboPaymentProvider, - wallet: context.read().currentUser!.wallet, + wallet: context.read().currentUser.wallet, supportedCountriesRetriever: turboSupportedCountriesRetriever, ); diff --git a/lib/user/download_wallet/bloc/download_wallet_bloc.dart b/lib/user/download_wallet/bloc/download_wallet_bloc.dart index e53b178afe..2fe6953f02 100644 --- a/lib/user/download_wallet/bloc/download_wallet_bloc.dart +++ b/lib/user/download_wallet/bloc/download_wallet_bloc.dart @@ -28,7 +28,7 @@ class DownloadWalletBloc return; } try { - final wallet = _ardriveAuth.currentUser!.wallet; + final wallet = _ardriveAuth.currentUser.wallet; await _ardriveIOUtils.downloadWalletAsJsonFile( wallet: wallet, diff --git a/lib/utils/user_utils.dart b/lib/utils/user_utils.dart index fa2353dfaf..e070f51eb0 100644 --- a/lib/utils/user_utils.dart +++ b/lib/utils/user_utils.dart @@ -4,7 +4,7 @@ import 'package:ardrive/user/user.dart'; bool isDriveOwner(ArDriveAuth auth, String driveOwner) { User user; try { - user = auth.currentUser!; + user = auth.currentUser; } catch (e) { return false; } diff --git a/test/authentication/ardrive_auth_test.dart b/test/authentication/ardrive_auth_test.dart index b99a7c9876..5a80824b04 100644 --- a/test/authentication/ardrive_auth_test.dart +++ b/test/authentication/ardrive_auth_test.dart @@ -55,6 +55,13 @@ void main() { id: 'some_id', rootFolderId: 'some_id', )); + + when(() => mockArConnectService.checkPermissions()).thenAnswer( + (invocation) => Future.value(true), + ); + when(() => mockArConnectService.disconnect()).thenAnswer( + (invocation) => Future.value(null), + ); }); // test `ArDriveAuth` @@ -643,7 +650,7 @@ void main() { when(() => mockArweaveService.getFirstPrivateDriveTxId(wallet, maxRetries: any(named: 'maxRetries'))) .thenAnswer((_) async => 'some_id'); - // mock cripto derive drive key + // mock crypto derive drive key when( () => mockArDriveCrypto.deriveDriveKey( wallet, diff --git a/test/utils/user_utils.dart b/test/utils/user_utils.dart index 9fc96b60ba..cd0fd68a99 100644 --- a/test/utils/user_utils.dart +++ b/test/utils/user_utils.dart @@ -59,7 +59,7 @@ void main() { test('should return false when the current user is null', () { // arrange - when(() => auth.currentUser).thenReturn(null); + when(() => auth.currentUser).thenThrow(Exception()); // act final result = isDriveOwner(auth, 'other address');