Skip to content

Commit

Permalink
Redirects issue fixed for auth flow (#7)
Browse files Browse the repository at this point in the history
* Redirects added for navigation based on auth session

* Movie details passes through provider instead of constructor

* Feature to allow user directly go to home after signup added

* Redirects issue fixed
  • Loading branch information
roshandroids authored Dec 30, 2022
1 parent 8bc7787 commit f08d170
Show file tree
Hide file tree
Showing 19 changed files with 279 additions and 138 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A simple mobile client app for browsing movies listed under [YTS website](https:
- [Plugins and Packages used](#plugins-and-packages-used)

- [Previews](#previews)
- [How to contribute](#how-to-contribute)

## App Architecture and Folder Structure

Expand Down Expand Up @@ -143,7 +144,6 @@ lib
##### Others utilities :

- [url_launcher: ^6.1.7](https://pub.dev/packages/url_launcher)
- [stack_trace: ^1.10.0](https://pub.dev/packages/stack_trace)
- [shimmer: ^2.0.0](https://pub.dev/packages/shimmer)
- [pull_to_refresh: ^2.0.0](https://pub.dev/packages/pull_to_refresh)
- [flutter_keyboard_visibility: ^5.4.0](https://pub.dev/packages/flutter_keyboard_visibility)
Expand Down Expand Up @@ -178,3 +178,11 @@ lib
</td>
</tr>
</table>

# How to contribute

<p align="center">
🚧 This project is still under development phase 🚧</p>

- If encountered any bugs feel free to report [here](https://github.com/flutterians/yts_mobile/issues)
- If you want to contribute to the project you can send [Pull request](https://github.com/flutterians/yts_mobile/pulls).
6 changes: 3 additions & 3 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -421,7 +421,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -470,7 +470,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
2 changes: 1 addition & 1 deletion lib/core/constants/enums.dart
Original file line number Diff line number Diff line change
@@ -1 +1 @@
enum SocialAuthType { facebook, google }
enum SocialAuthType { email, facebook, google }
150 changes: 75 additions & 75 deletions lib/core/routes/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,90 +6,90 @@ import 'package:yts_mobile/core/core.dart';
import 'package:yts_mobile/features/auth/auth.dart';
import 'package:yts_mobile/features/movies/movies.dart';

final goRouterProvider = Provider<GoRouter>((ref) {
// final authState = ref.watch(authProvider);
final key = GlobalKey<NavigatorState>();

return GoRouter(
initialLocation: RoutePaths.splashRoute.path,
navigatorKey: key,
routes: [
GoRoute(
path: RoutePaths.splashRoute.path,
name: RoutePaths.splashRoute.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const SplashScreen(),
),
redirect: (_, __) => RoutePaths.loginRoute.path,
),
GoRoute(
path: RoutePaths.loginRoute.path,
name: RoutePaths.loginRoute.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const LoginScreen(),
final goRouterProvider = Provider<GoRouter>(
(ref) {
final authState = ref.watch(authStatusProvider);
final key = GlobalKey<NavigatorState>();
return GoRouter(
initialLocation: RoutePaths.splashRoute.path,
navigatorKey: key,
routes: [
GoRoute(
path: RoutePaths.splashRoute.path,
name: RoutePaths.splashRoute.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const SplashScreen(),
),
// redirect: (_, __) => RoutePaths.loginRoute.path,
),
),
GoRoute(
path: RoutePaths.signupRoute.path,
name: RoutePaths.signupRoute.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const SignupScreen(),
GoRoute(
path: RoutePaths.loginRoute.path,
name: RoutePaths.loginRoute.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const LoginScreen(),
),
),
),
GoRoute(
path: RoutePaths.latestMovies.path,
name: RoutePaths.latestMovies.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const LatestMoviesPage(),
GoRoute(
path: RoutePaths.signupRoute.path,
name: RoutePaths.signupRoute.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const SignupScreen(),
),
),
routes: [
GoRoute(
path: RoutePaths.movieDetail.path,
name: RoutePaths.movieDetail.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: MovieDetailPage(
movieId: int.parse(state.params['id']!),
imgCoverUrl: state.extra as String?,
GoRoute(
path: RoutePaths.latestMovies.path,
name: RoutePaths.latestMovies.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: const LatestMoviesPage(),
),
routes: [
GoRoute(
path: RoutePaths.movieDetail.path,
name: RoutePaths.movieDetail.routeName,
pageBuilder: (context, state) => FadeTransitionPage(
key: state.pageKey,
child: ProviderScope(
overrides: [
currentMovieDetailItemProvider
.overrideWithValue(state.extra! as MovieModel)
],
child: const MovieDetailPage(),
),
),
),
),
],
),
],
redirect: (BuildContext context, GoRouterState state) {
return null;

// return RoutePaths.loginRoute.path;

// If our async state is loading, don't perform redirects, yet
// if (authState.isLoading || authState.hasError) return null;

// // Here we guarantee that hasData == true, i.e. we have a readable value
],
),
],
refreshListenable: authState,
redirect: (BuildContext context, GoRouterState state) {
final authenticated = authState.loggedInStatus;

// // This has to do with how the FirebaseAuth SDK handles the "log-in" state
// // Returning `null` means "we are not authorized"
// final isAuth = authState.valueOrNull != null;
final isSplash = state.location == RoutePaths.splashRoute.path;

// final isSplash = state.location == RoutePaths.splashRoute.path;
// if (isSplash) {
// return isAuth
// ? RoutePaths.latestMovies.path
// : RoutePaths.loginRoute.path;
// }
if (isSplash) {
return authenticated
? RoutePaths.latestMovies.path
: RoutePaths.loginRoute.path;
}

// final isLoggingIn = state.location == RoutePaths.loginRoute.path;
// if (isLoggingIn) return isAuth ? RoutePaths.latestMovies.path : null;
if (state.subloc == RoutePaths.signupRoute.path) {
return RoutePaths.signupRoute.path;
}

// return isAuth ? null : RoutePaths.splashRoute.path;
},
debugLogDiagnostics: kDebugMode,
);
});
final isLoggingIn = state.location == RoutePaths.loginRoute.path;
if (isLoggingIn) {
return authenticated ? RoutePaths.latestMovies.path : null;
}
return null;
},
debugLogDiagnostics: kDebugMode,
);
},
);

class FadeTransitionPage extends CustomTransitionPage<void> {
FadeTransitionPage({
Expand Down
34 changes: 34 additions & 0 deletions lib/core/widgets/logout_alert_dialogue.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:yts_mobile/core/core.dart';

class LogoutAlertDialogue {
static Future<bool?> showAlert(
BuildContext context,
) async {
return showDialog<bool?>(
context: context,
useRootNavigator: true,
barrierDismissible: false,
builder: (context) => AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
title: Text('Logout ?'.hardcoded),
content: const Text(''),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.red,
),
child: Text('Confirm'.hardcoded),
onPressed: () => Navigator.pop(context, true),
),
TextButton(
child: Text('Cancel'.hardcoded),
onPressed: () => Navigator.pop(context, false),
),
],
),
);
}
}
1 change: 1 addition & 0 deletions lib/core/widgets/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export 'custom_text_field.dart';
export 'error_view.dart';
export 'grid_item_shimmer.dart';
export 'loaders/loaders.dart';
export 'logout_alert_dialogue.dart';
export 'shimmer.dart';
12 changes: 12 additions & 0 deletions lib/features/auth/application/auth_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ final socialLoginControllerProvider = StateNotifierProvider.autoDispose<
AuthController<UserModel>, BaseState<dynamic>>(
_authController,
);
final logoutControllerProvider = StateNotifierProvider.autoDispose<
AuthController<UserModel>, BaseState<dynamic>>(
_authController,
);
AuthController<T> _authController<T>(Ref ref) {
final authRepository = ref.watch(authRepositoryProvider);
return AuthController<T>(ref, authRepository);
Expand Down Expand Up @@ -69,4 +73,12 @@ class AuthController<T> extends StateNotifier<BaseState<dynamic>> {
BaseState.error,
);
}

/// [logout] logout user from the app
Future<void> logout() async {
state = const BaseState<void>.loading();
await authRepository.logout().then(
(value) => ref.read(storageServiceProvider).remove('SocialAuthType'),
);
}
}
34 changes: 32 additions & 2 deletions lib/features/auth/application/auth_status_provider.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
import 'dart:async';

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:yts_mobile/features/auth/auth.dart';

final authStatusProvider = StreamProvider<User?>((ref) {
return ref.read(firebaseAuthProvider).authStateChanges();
final authStatusProvider = ChangeNotifierProvider<GoRouterRefreshStream>((ref) {
return GoRouterRefreshStream(
ref.read(firebaseAuthProvider).authStateChanges(),
);
});
final skippedProvider = StateProvider<bool>((ref) {
return false;
});

class GoRouterRefreshStream extends ChangeNotifier {
GoRouterRefreshStream(Stream<User?> stream) {
notifyListeners();
_subscription = stream.asBroadcastStream().listen(
(user) {
_isLoggedIn = user != null;
notifyListeners();
},
);
}

late final StreamSubscription<User?> _subscription;
bool _isLoggedIn = false;
bool get loggedInStatus => _isLoggedIn;

@override
void dispose() {
_subscription.cancel();
super.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import 'package:yts_mobile/features/auth/auth.dart';
final authRepositoryProvider = Provider<AuthRepository>(
(ref) {
final firebaseAuth = ref.watch(firebaseAuthProvider);
return AuthRepositoryImpl(firebaseAuth);
final storageService = ref.watch(storageServiceProvider);
return AuthRepositoryImpl(firebaseAuth, storageService);
},
);

Expand All @@ -27,4 +28,7 @@ abstract class AuthRepository {
Future<Either<UserModel, Failure>> loginWithSocialAuth({
required SocialAuthType socialAuthType,
});

/// [logout] login user with google account
Future<Either<bool, Failure>> logout();
}
Loading

0 comments on commit f08d170

Please sign in to comment.