Skip to content

Commit

Permalink
Merge pull request #1477 from ardriveapp/arconnect_issue
Browse files Browse the repository at this point in the history
PE-5015: ArConnect popup partially logging out users
  • Loading branch information
thiagocarvalhodev authored Nov 27, 2023
2 parents 46daf10 + 7599e0e commit 20703df
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 74 deletions.
54 changes: 31 additions & 23 deletions lib/app_shell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,30 +31,35 @@ class AppShell extends StatefulWidget {
class AppShellState extends State<AppShell> {
bool _showProfileOverlay = false;
bool _showWalletSwitchDialog = true;

@override
void initState() {
onArConnectWalletSwitch(() {
context.read<ProfileCubit>().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;
},
);
});

super.initState();
}

@override
Widget build(BuildContext context) => BlocBuilder<DrivesCubit, DrivesState>(
builder: (context, _) {
onArConnectWalletSwitch(() {
context
.read<ProfileCubit>()
.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(
child: BlocBuilder<SyncCubit, SyncState>(
builder: (context, syncState) => syncState is SyncInProgress
Expand All @@ -76,6 +81,8 @@ class AppShellState extends State<AppShell> {
.isCurrentProfileArConnect(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
final isCurrentProfileArConnect =
snapshot.data == true;
return Align(
alignment: Alignment.center,
child: Material(
Expand All @@ -92,7 +99,8 @@ class AppShellState extends State<AppShell> {
Text(appLocalizationsOf(
context)
.syncProgressPercentage(
(syncProgress.progress *
(syncProgress
.progress *
100)
.roundToDouble()
.toString()))),
Expand All @@ -118,7 +126,7 @@ class AppShellState extends State<AppShell> {
.buttonNormalBold(),
),
),
title: snapshot.data ?? false
title: isCurrentProfileArConnect
? appLocalizationsOf(context)
.syncingPleaseRemainOnThisTab
: appLocalizationsOf(context)
Expand Down
4 changes: 3 additions & 1 deletion lib/authentication/ardrive_auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class ArDriveAuthImpl implements ArDriveAuth {
return _currentUser!;
}

@visibleForTesting
set currentUser(User? user) {
_currentUser = user;
}
Expand Down Expand Up @@ -226,8 +227,9 @@ class ArDriveAuthImpl implements ArDriveAuth {
_userStreamController.add(null);
}

await _userRepository.deleteUser();
await _databaseHelpers.deleteAllTables();
(await _metadataCache).clear();
await (await _metadataCache).clear();
} catch (e) {
logger.e('Failed to logout user', e);
throw AuthenticationFailedException('Failed to logout user');
Expand Down
27 changes: 17 additions & 10 deletions lib/authentication/login/blocs/login_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,16 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
CheckIfUserIsLoggedIn event,
Emitter<LoginState> emit,
) async {
logger.d('Checking if user is logged in');

emit(LoginLoading());

if (await _arDriveAuth.isUserLoggedIn()) {
logger.d('User is logged in');

if (await _arDriveAuth.isBiometricsEnabled()) {
logger.d('Biometrics is enabled');

try {
await _loginWithBiometrics(emit: emit);
return;
Expand All @@ -173,14 +179,15 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
}
}
emit(const PromptPassword());

return;
}

if (event.gettingStarted) {
_handleCreateNewWalletEvent(const CreateNewWallet(), emit);
} else {
emit(LoginInitial(_arConnectService.isExtensionPresent()));
emit(LoginInitial(
isArConnectAvailable: _arConnectService.isExtensionPresent(),
));
}
}

Expand Down Expand Up @@ -279,7 +286,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
await _arDriveAuth.logout();
}

emit(LoginInitial(_arConnectService.isExtensionPresent()));
emit(LoginInitial(
isArConnectAvailable: _arConnectService.isExtensionPresent(),
));
}

Future<void> _handleFinishOnboardingEvent(
Expand Down Expand Up @@ -330,7 +339,10 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
onArConnectWalletSwitch(() async {
final isUserLoggedIng = await _arDriveAuth.isUserLoggedIn();
if (isUserLoggedIng && !_isArConnectWallet()) {
logger.d('Wallet switch detected. Is current profile ArConnect: false');
logger.d(
'Wallet switch detected for non-arconnect wallet'
' ($profileType) - ignoring',
);
return;
}

Expand All @@ -339,14 +351,9 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
return;
}

logger.i('ArConnect wallet switched');
// ignore: invalid_use_of_visible_for_testing_member
emit(const LoginFailure(WalletMismatchException()));

await _arDriveAuth.logout();

// ignore: invalid_use_of_visible_for_testing_member
emit(const LoginInitial(true));
logger.i('ArConnect wallet switched');
});
}

Expand Down
4 changes: 2 additions & 2 deletions lib/authentication/login/blocs/login_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ abstract class LoginState extends Equatable {
}

class LoginInitial extends LoginState {
const LoginInitial(this.isArConnectAvailable);

final bool isArConnectAvailable;

const LoginInitial({required this.isArConnectAvailable});
}

class LoginLoading extends LoginState {}
Expand Down
26 changes: 10 additions & 16 deletions lib/authentication/login/views/login_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ class _LoginPageState extends State<LoginPage> {
if (loginState is LoginOnBoarding) {
preCacheOnBoardingAssets(context);
} else if (loginState is LoginFailure) {
// TODO: Verify if the error is `NoConnectionException` and show an appropriate message after validating with UI/UX
// TODO: Verify if the error is `NoConnectionException` and show an
/// appropriate message after validating with UI/UX
logger.e('Login Failure', loginState.error);

Expand Down Expand Up @@ -129,7 +130,9 @@ class _LoginPageState extends State<LoginPage> {
logger.d('Login Success, unlocking default profile');

context.read<ProfileCubit>().unlockDefaultProfile(
loginState.user.password, loginState.user.profileType);
loginState.user.password,
loginState.user.profileType,
);
}
},
builder: (context, loginState) {
Expand Down Expand Up @@ -340,14 +343,6 @@ class _LoginPageScaffoldState extends State<LoginPageScaffold> {
final isOnBoarding = current is LoginOnBoarding;
final isCreateNewWallet = current is LoginCreateNewWallet;

logger.d(
'LoginBloc buildWhen'
' - isFailure: $isFailure'
' - isSuccess: $isSuccess'
' - isOnBoarding: $isOnBoarding'
' - isCreateNewWallet: $isCreateNewWallet',
);

return !(isFailure || isSuccess || isOnBoarding || isCreateNewWallet);
},
builder: (context, loginState) {
Expand Down Expand Up @@ -405,13 +400,13 @@ class _LoginPageScaffoldState extends State<LoginPageScaffold> {
}

class PromptWalletView extends StatefulWidget {
final bool isArConnectAvailable;

const PromptWalletView({
super.key,
required this.isArConnectAvailable,
});

final bool isArConnectAvailable;

@override
State<PromptWalletView> createState() => _PromptWalletViewState();
}
Expand Down Expand Up @@ -732,17 +727,16 @@ class PromptPasswordView extends StatefulWidget {
}

class _PromptPasswordViewState extends State<PromptPasswordView> {
final _passwordController = TextEditingController();
bool _isPasswordValid = false;

@override
void initState() {
super.initState();

PlausibleEventTracker.track(event: PlausibleEvent.welcomeBackPage);
}

final _passwordController = TextEditingController();

bool _isPasswordValid = false;

@override
Widget build(BuildContext context) {
return MaxDeviceSizesConstrainedBox(
Expand Down
10 changes: 8 additions & 2 deletions lib/components/wallet_switch_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// ignore_for_file: use_build_context_synchronously

import 'package:ardrive/authentication/ardrive_auth.dart';
import 'package:ardrive/blocs/blocs.dart';
import 'package:ardrive/components/app_dialog.dart';
import 'package:ardrive/utils/app_localizations_wrapper.dart';
Expand All @@ -22,9 +25,12 @@ class WalletSwitchDialog extends StatelessWidget {
),
actions: [
TextButton(
onPressed: () {
onPressed: () async {
await context.read<ArDriveAuth>().logout();
await context.read<ProfileCubit>().logoutProfile();

Navigator.pop(context);
context.read<ProfileCubit>().logoutProfile();

if (fromAuthPage) {
triggerHTMLPageReload();
context.read<ProfileAddCubit>().promptForWallet();
Expand Down
2 changes: 1 addition & 1 deletion lib/user/repositories/user_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class _UserRepository implements UserRepository {
return profile != null;
}

// Will return null if no user is not logged in - i.e. not present in the DB
// Will return null if no user is logged in - i.e. not present in the DB
@override
Future<String?> getOwnerOfDefaultProfile() async {
final profile = await _profileDao.getDefaultProfile();
Expand Down
2 changes: 1 addition & 1 deletion packages/ardrive_utils/lib/src/html/html_util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class TabVisibilitySingleton {
}

// TODO: Move this code to the arconnect package.
void onArConnectWalletSwitch(Function onWalletSwitch) =>
Function() onArConnectWalletSwitch(Function onWalletSwitch) =>
implementation.onWalletSwitch(onWalletSwitch);

void triggerHTMLPageReload() => implementation.reload();
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ StreamSubscription onTabGetsFocused(Function onFocus) {
return emptyStream.listen((event) {});
}

void onWalletSwitch(Function onSwitch) {
return;
Function() onWalletSwitch(Function onSwitch) {
return () => null;
}

void reload() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ StreamSubscription<Event> onTabGetsFocused(Function onFocus) {
return subscription;
}

void onWalletSwitch(Function onWalletSwitch) {
window.addEventListener('walletSwitch', (event) {
onWalletSwitch();
});
Function() onWalletSwitch(Function callback) {
listener(event) => callback();

window.addEventListener('walletSwitch', listener);
return () => window.removeEventListener('walletSwitch', listener);
}

void reload() {
Expand Down
14 changes: 5 additions & 9 deletions test/authentication/ardrive_auth_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -531,15 +531,17 @@ void main() {
() async {
when(() => mockUserRepository.hasUser())
.thenAnswer((invocation) => Future.value(false));

when(() => mockDatabaseHelpers.deleteAllTables())
.thenAnswer((invocation) async {});
when(() => mockUserRepository.deleteUser())
.thenAnswer((invocation) async {});

await arDriveAuth.logout();

verifyNever(() => mockSecureKeyValueStore.remove('password'));
verifyNever(() => mockSecureKeyValueStore.remove('biometricEnabled'));
verify(() => mockDatabaseHelpers.deleteAllTables()).called(1);
verify(() => mockUserRepository.deleteUser()).called(1);
expect(() => arDriveAuth.currentUser,
throwsA(isA<AuthenticationUserIsNotLoggedInException>()));
});
Expand All @@ -556,38 +558,30 @@ void main() {
when(() => mockArweaveService.getFirstPrivateDriveTxId(wallet,
maxRetries: any(named: 'maxRetries')))
.thenAnswer((_) async => 'some_id');

when(() => mockBiometricAuthentication.isEnabled())
.thenAnswer((_) async => false);

when(
() => mockArDriveCrypto.deriveDriveKey(
wallet,
any(),
any(),
),
).thenAnswer((invocation) => Future.value(SecretKey([])));

when(() => mockUserRepository.hasUser())
.thenAnswer((invocation) => Future.value(true));

when(() => mockArweaveService.getLatestDriveEntityWithId(
any(), any(), any()))
.thenAnswer((invocation) => Future.value(DriveEntity(
id: 'some_id',
rootFolderId: 'some_id',
)));

when(() => mockUserRepository.deleteUser())
.thenAnswer((invocation) async {});

when(() => mockUserRepository.saveUser(
'password', ProfileType.json, wallet))
.thenAnswer((invocation) => Future.value(null));

when(() => mockUserRepository.getUser('password'))
.thenAnswer((invocation) async => loggedUser);

when(() => mockUserRepository.deleteUser())
.thenAnswer((invocation) async {});
when(() => mockSecureKeyValueStore.remove('password'))
Expand All @@ -599,6 +593,8 @@ void main() {

await arDriveAuth.login(wallet, 'password', ProfileType.json);

verify(() => mockUserRepository.deleteUser()).called(1);

await arDriveAuth.logout();

expect(arDriveAuth.firstPrivateDriveTxId, isNull);
Expand Down
Loading

0 comments on commit 20703df

Please sign in to comment.