Skip to content

Commit

Permalink
Merge pull request #409 from wizlif/fix/auth-routing
Browse files Browse the repository at this point in the history
Fix/auth-routing
  • Loading branch information
Xazin committed Jul 4, 2023
2 parents 72deaa7 + e06c70d commit b2206de
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 33 deletions.
7 changes: 4 additions & 3 deletions lib/core/config/network_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ part of '../core.dart';

/// Manage all url creation and building
// base static url
final baseStaticUrl = '${dotenv.get('BASE_STATIC_ENDPOINT_URL')}/\$web';
final baseStaticUrl =
Uri.parse(dotenv.get('BASE_STATIC_ENDPOINT_URL')).resolve('\$web');

final nullStaticUrl = '$baseStaticUrl/${null}';
final nullStaticUrl = baseStaticUrl.resolve('null').toString();

extension ImageExtension on String {
String get imageUrl => '$baseStaticUrl/$this';
String get imageUrl => baseStaticUrl.resolve(this).toString();
}
9 changes: 8 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

import 'application/auth/auth_bloc.dart';
import 'domain/core/i_settings_repository.dart';
import 'infrastructure/core/injection.dart';
import 'presentation/core/app_widget.dart';
import 'presentation/routes/app_routes.dart';

Future<void> main() async {
runZonedGuarded(
Expand All @@ -20,7 +22,12 @@ Future<void> main() async {
await Firebase.initializeApp();
await dotenv.load();

runApp(AppWidget());
final appRouter = AppRouter(
authBloc: getIt<AuthBloc>(),
settingsRepo: getIt<ISettingsRepository>(),
);

runApp(AppWidget(appRouter: appRouter));
},
(error, stack) => FirebaseCrashlytics.instance.recordError(error, stack),
);
Expand Down
14 changes: 0 additions & 14 deletions lib/presentation/auth/unauthenticated_screen.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import '../../domain/core/i_settings_repository.dart';
import '../../infrastructure/core/injection.dart';
import '../routes/app_routes.dart';
import '../shared_widgets/pill_button.dart';
import '../themes/constants.dart';

class UnauthenticatedPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
checkAndMaybeShowOnboarding(context);
});

return Scaffold(
backgroundColor: kAccentColor,
body: Container(
Expand Down Expand Up @@ -61,12 +55,4 @@ class UnauthenticatedPage extends StatelessWidget {
),
);
}

Future<void> checkAndMaybeShowOnboarding(BuildContext context) async {
// Push onboarding screen if first time launching application
final settingsRepository = getIt<ISettingsRepository>();
if (!(await settingsRepository.getWasUserOnboarded())) {
context.push(AppPage.onBoarding.path);
}
}
}
12 changes: 6 additions & 6 deletions lib/presentation/core/app_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import '../routes/app_routes.dart';
import '../themes/themes.dart';

class AppWidget extends StatelessWidget {
final _appRouter = AppRouter();
final AppRouter appRouter;

const AppWidget({super.key, required this.appRouter});

@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthBloc>(
create: (_) => getIt<AuthBloc>()..add(AuthEvent.authCheckRequested()),
create: (_) =>
appRouter.authBloc..add(AuthEvent.authCheckRequested()),
),
BlocProvider<ProfileBloc>(create: (_) => getIt<ProfileBloc>()),
BlocProvider<ProfileTabBloc>(create: (_) => getIt<ProfileTabBloc>())
Expand All @@ -35,17 +38,14 @@ class AppWidget extends StatelessWidget {
BlocProvider.of<ProfileTabBloc>(context)
.add(FetchProfileTabInfo());
},
unauthenticated: () {
_appRouter.router.go(AppPage.unauthenticated.path);
},
orElse: () {},
);
},
child: MaterialApp.router(
color: Colors.white,
title: 'CollAction',
theme: lightTheme(),
routerConfig: _appRouter.router,
routerConfig: appRouter.router,
),
),
);
Expand Down
37 changes: 36 additions & 1 deletion lib/presentation/routes/app_routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import '../../../presentation/profile/profile_screen.dart';
import '../../application/auth/auth_bloc.dart';
import '../../domain/core/i_settings_repository.dart';
import '../auth/auth_screen.dart';
import '../auth/unauthenticated_screen.dart';
import '../auth/widgets/verified.dart';
Expand All @@ -19,13 +21,24 @@ import '../onboarding/onboarding_screen.dart';
import '../settings/settings_layout.dart';
import '../settings/settings_screen.dart';
import '../shared_widgets/web_view_page.dart';
import 'refresh_stream.dart';

part 'app_pages.dart';

class AppRouter {
final AuthBloc authBloc;
final ISettingsRepository settingsRepo;

final GlobalKey<NavigatorState> _rootNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'root');

final _unauthenticatedPaths = [
AppPage.onBoarding.path,
AppPage.unauthenticated.path,
AppPage.auth.path,
AppPage.verified.path,
];

GoRouter get router => GoRouter(
navigatorKey: _rootNavigatorKey,
routes: [
Expand Down Expand Up @@ -145,7 +158,29 @@ class AppRouter {
],
debugLogDiagnostics: true,
initialLocation: AppPage.home.path,
refreshListenable: GoRouterAuthRefreshStream(authBloc.stream),
redirect: (context, state) async {
if (_unauthenticatedPaths.contains(state.location)) {
return null;
}

final path = await authBloc.state.mapOrNull(
unauthenticated: (_) async {
final isOnboarded = await settingsRepo.getWasUserOnboarded();
if (!isOnboarded) {
return AppPage.onBoarding.path;
}

return AppPage.unauthenticated.path;
},
);

return path;
},
);

AppRouter();
AppRouter({
required this.authBloc,
required this.settingsRepo,
});
}
27 changes: 27 additions & 0 deletions lib/presentation/routes/refresh_stream.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'dart:async';

import 'package:flutter/material.dart';

import '../../application/auth/auth_bloc.dart';

class GoRouterAuthRefreshStream extends ChangeNotifier {
late final StreamSubscription<AuthState> _subscription;

GoRouterAuthRefreshStream(Stream<AuthState> stream) {
notifyListeners();
_subscription = stream.asBroadcastStream().listen((state) {
// Only notify router to rebuild on following conditions
state.maybeWhen(
authenticated: (_) => notifyListeners(),
loggedIn: (_) => notifyListeners(),
unauthenticated: () => notifyListeners(),
orElse: () {});
});
}

@override
void dispose() {
_subscription.cancel();
super.dispose();
}
}
21 changes: 17 additions & 4 deletions test/core/config/network.config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ void main() {
'when the user profile is not null', () async {
// arrange
final userProfile = testUserProfile;

// act
final userAvatarUrl = userProfile.avatarUrl;

// assert
expect(
userAvatarUrl,
equals('$baseStaticUrl/${userProfile.profile.avatar}'),
equals(
Uri.parse(baseStaticUrl)
.resolve(userProfile.profile.avatar)
.toString(),
),
);
});

Expand Down Expand Up @@ -65,7 +70,11 @@ void main() {
// assert
expect(
crowdActionBannerUrl,
equals('$baseStaticUrl/${crowdAction.images.banner}'),
equals(
Uri.parse(baseStaticUrl)
.resolve(crowdAction.images.banner)
.toString(),
),
);
});

Expand Down Expand Up @@ -99,7 +108,9 @@ void main() {
// assert
expect(
crowdActionCardUrl,
equals('$baseStaticUrl/${crowdAction.images.card}'),
equals(
Uri.parse(baseStaticUrl).resolve(crowdAction.images.card).toString(),
),
);
});

Expand Down Expand Up @@ -133,7 +144,9 @@ void main() {
// assert
expect(
participationAvatarUrl,
equals('$baseStaticUrl/${participation.avatar}'),
equals(
Uri.parse(baseStaticUrl).resolve(participation.avatar).toString(),
),
);
});

Expand Down
20 changes: 18 additions & 2 deletions test/main.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import 'package:collaction_app/application/auth/auth_bloc.dart';
import 'package:collaction_app/domain/core/i_settings_repository.dart';
import 'package:collaction_app/presentation/core/app_widget.dart';
import 'package:collaction_app/presentation/routes/app_routes.dart';
import 'package:flutter_test/flutter_test.dart';

import 'application/auth/auth_bloc.mocks.dart';
import 'test_utilities.dart';

void main() {
setUpAll(() {});
late AppRouter appRouter;
late AuthBloc authBloc;
late ISettingsRepository settingsRepo;

setUpAll(() {
authBloc = MockAuthBloc();
settingsRepo = MockSettingsRepository();
appRouter = AppRouter(authBloc: authBloc, settingsRepo: settingsRepo);
});

testWidgets('Can render AppWidget', (WidgetTester tester) async {
await tester.pumpWidget(AppWidget());
await tester.pumpWidget(AppWidget(
appRouter: appRouter,
));

expect(find.byType(AppWidget), findsOneWidget);
});
Expand Down
8 changes: 7 additions & 1 deletion test/presentation/home/home_screen_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:collaction_app/application/auth/auth_bloc.dart';
import 'package:collaction_app/application/crowdaction/spotlight/spotlight_bloc.dart';
import 'package:collaction_app/domain/core/i_settings_repository.dart';
import 'package:collaction_app/domain/crowdaction/crowdaction_failures.dart';
Expand All @@ -9,6 +10,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:mocktail/mocktail.dart';

import '../../application/auth/auth_bloc.mocks.dart';
import '../../test_helper.dart';
import '../../test_utilities.dart';

Expand All @@ -17,12 +19,16 @@ void main() {
late final MockSettingsRepository settingsRepository;
late final MockCrowdActionRepository crowdActionRepository;
late SpotlightBloc spotlightBloc;
late AuthBloc authBloc;

setUpAll(() {
appRouter = AppRouter();
settingsRepository = MockSettingsRepository();
crowdActionRepository = MockCrowdActionRepository();
spotlightBloc = SpotlightBloc(crowdActionRepository);
authBloc = MockAuthBloc();
when(() => authBloc.state).thenAnswer((_) => AuthState.initial());

appRouter = AppRouter(authBloc: authBloc, settingsRepo: settingsRepository);

GetIt.I.registerSingleton<SpotlightBloc>(spotlightBloc);
GetIt.I.registerSingleton<ISettingsRepository>(settingsRepository);
Expand Down
5 changes: 4 additions & 1 deletion test/presentation/profile/profile_screen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'package:mocktail/mocktail.dart';
import '../../application/auth/auth_bloc.mocks.dart';
import '../../test_helper.dart';
import '../../test_utilities.dart';
import '../../utils/user.fixtures.dart';

void main() {
late final AppRouter appRouter;
Expand All @@ -34,12 +35,12 @@ void main() {
late MockAuthBloc authBloc;

setUpAll(() {
appRouter = AppRouter();
settingsRepository = MockSettingsRepository();
crowdActionRepository = MockCrowdActionRepository();
profileRepository = MockProfileRepository();
avatarRepository = MockAvatarRepository();
authBloc = MockAuthBloc();
appRouter = AppRouter(authBloc: authBloc, settingsRepo: settingsRepository);
spotlightBloc = SpotlightBloc(crowdActionRepository);
profileBloc = ProfileBloc(profileRepository, avatarRepository);
profileTabBloc = ProfileTabBloc(crowdActionRepository);
Expand Down Expand Up @@ -82,6 +83,8 @@ void main() {
testWidgets('navigate to profile screen', (tester) async {
when(() => settingsRepository.getWasUserOnboarded())
.thenAnswer((_) => Future.value(true));
when(() => authBloc.state)
.thenAnswer((_) => AuthState.authenticated(testUser));

await buildAndPump(
tester: tester,
Expand Down

0 comments on commit b2206de

Please sign in to comment.