Skip to content

Commit

Permalink
✨ Add: authentication blocker (#66)
Browse files Browse the repository at this point in the history
* Refactor home state and UI classes

* Refactor network error page layout

* Fix response handling and URL generation in media
classes

* Refactor generateGreeting function to use a
constant array

* Remove shorebird.yaml from assets

* Add session cubit and sign out functionality

* Fix UI overlay style and sign out on error

* Add login page and update router configuration

* Add login page UI and input validators

* Refactor login page UI and add form validation

* Add sign in functionality to login page

* Add FilledInputField widget

* Add session authentication features and
login/signup pages

* Add signup route and page

* Add Google SVG icon and update UI in AuthPage

* Refactor button styles and text formatting

* Delete unused assets
  • Loading branch information
devaryakjha authored Nov 18, 2023
1 parent 8b1116d commit a5011c5
Show file tree
Hide file tree
Showing 32 changed files with 833 additions and 215 deletions.
Binary file removed assets/icon/Integrate-with-Samsung-IAP-05212021.pdf
Binary file not shown.
Binary file removed assets/icon/app_icon.jpg
Binary file not shown.
1 change: 1 addition & 0 deletions assets/icon/google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/images/error/something_went_wrong.png
Binary file not shown.
1 change: 1 addition & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extensions:
2 changes: 2 additions & 0 deletions lib/app.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:varanasi_mobile_app/features/library/cubit/library_cubit.dart';
import 'package:varanasi_mobile_app/features/session/cubit/session_cubit.dart';
import 'package:varanasi_mobile_app/features/user-library/cubit/user_library_cubit.dart';
import 'package:varanasi_mobile_app/utils/constants/constants.dart';
import 'package:varanasi_mobile_app/utils/router.dart';
Expand Down Expand Up @@ -36,6 +37,7 @@ class Varanasi extends StatelessWidget {
create: (ctx) => MediaPlayerCubit()..init(),
),
BlocProvider(lazy: false, create: (ctx) => LibraryCubit()),
BlocProvider(lazy: false, create: (_) => SessionCubit()..init()),
],
child: Builder(builder: (context) {
final scheme = context.select(
Expand Down
5 changes: 4 additions & 1 deletion lib/cubits/player/player_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ class MediaPlayerCubit extends AppCubit<MediaPlayerState>
media.moreInfoUrl,
options: CommonOptions(
transformer: (response) {
final data = response as List<dynamic>;
if (response is! List<dynamic>) {
throw Exception('Invalid response');
}
final data = response;
if (data.isEmpty) {
throw Exception('No data found');
}
Expand Down
2 changes: 1 addition & 1 deletion lib/features/home/bloc/home_state.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
part of 'home_bloc.dart';

abstract class HomeState extends Equatable {
sealed class HomeState extends Equatable {
const HomeState();
}

Expand Down
8 changes: 4 additions & 4 deletions lib/features/home/ui/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ class HomePage extends StatelessWidget {
],
),
body: TriStateVisibility(
state: switch (state.runtimeType) {
HomeErrorState => TriState.error,
HomeLoadingState => TriState.loading,
_ => TriState.loaded,
state: switch (state) {
(HomeErrorState _) => TriState.error,
(HomeLoadingState _) => TriState.loading,
(_) => TriState.loaded,
},
loadingChild: const HomePageLoader(),
errorChild: switch (state) {
Expand Down
5 changes: 1 addition & 4 deletions lib/features/search/data/search_result/result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Result extends PlayableMedia with EquatableMixin {
String? get artworkUrl => image?.lastOrNull?.link ?? '';

@override
String get itemId => itemType.isSong
String get itemId => itemType.isSong && itemUrl.isNotEmpty
? (itemUrl.split('/').lastOrNull ?? id ?? '')
: (id ?? '');

Expand All @@ -98,7 +98,4 @@ class Result extends PlayableMedia with EquatableMixin {

@override
String get itemUrl => url ?? '';

@override
bool get preferLinkOverId => itemType.isSong || itemType.isAlbum;
}
7 changes: 3 additions & 4 deletions lib/features/search/data/top_search_result/top_search.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ class TopSearch extends PlayableMedia with EquatableMixin {
String? get artworkUrl => image?.lastOrNull?.link;

@override
String get itemId => itemUrl.split('/').lastOrNull ?? id ?? '';
String get itemId => itemUrl.isEmpty
? (id ?? '')
: (itemUrl.split('/').lastOrNull ?? id ?? '');

@override
String get itemSubtitle => "Top Search • $description";
Expand All @@ -83,7 +85,4 @@ class TopSearch extends PlayableMedia with EquatableMixin {

@override
String get itemUrl => url ?? '';

@override
bool get preferLinkOverId => true;
}
94 changes: 74 additions & 20 deletions lib/features/session/cubit/session_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import 'dart:async';

import 'package:equatable/equatable.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:go_router/go_router.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:varanasi_mobile_app/utils/app_cubit.dart';
import 'package:varanasi_mobile_app/utils/extensions/router.dart';
import 'package:varanasi_mobile_app/utils/helpers/get_app_context.dart';
import 'package:varanasi_mobile_app/utils/app_snackbar.dart';
import 'package:varanasi_mobile_app/utils/logger.dart';
import 'package:varanasi_mobile_app/utils/routes.dart';

part 'session_state.dart';

Expand All @@ -23,22 +20,9 @@ class SessionCubit extends AppCubit<SessionState> {

@override
FutureOr<void> init() {
_auth.userChanges().listen((user) {
if (user == null) {
emit(UnAuthenticated());
} else {
emit(Authenticated(user: user));
}
});
stream.distinct().listen((state) {
if (state is! Authenticated) {
return appContext.go(AppRoutes.authentication.path);
}
if ([AppRoutes.authentication.path]
.contains(appContext.routerState.matchedLocation)) {
return appContext.go(AppRoutes.home.path);
}
});
_auth.userChanges().map((user) {
return user == null ? UnAuthenticated() : Authenticated(user: user);
}).listen(emit);
}

Future<void> continueWithGoogle() async {
Expand All @@ -52,8 +36,78 @@ class SessionCubit extends AppCubit<SessionState> {
);
final userCredential = await _auth.signInWithCredential(credential);
_logger.d(userCredential.user?.toString());
} on FirebaseAuthException catch (e) {
_handleException(e);
} catch (e) {
await _googleSignIn.signOut();
_logger.d(e.toString());
}
}

Future<void> signInWithEmailAndPassword({
required String email,
required String password,
}) async {
emit(Authenticating());
try {
final userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
_logger.d(userCredential.user?.toString());
} on FirebaseAuthException catch (e) {
_handleException(e);
} catch (e) {
_logger.d(e.toString());
}
}

Future<void> signUpWithEmailAndPassword({
required String email,
required String password,
required String name,
}) async {
emit(Authenticating());
try {
final userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await userCredential.user?.updateDisplayName(name);
_logger.d(userCredential.user?.toString());
} on FirebaseAuthException catch (e) {
_handleException(e);
} catch (e) {
_logger.d(e.toString());
}
}

Future<void> signOut() async {
emit(Authenticating());
try {
await _googleSignIn.signOut();
await _auth.signOut();
} catch (e) {
_logger.d(e.toString());
}
}

void _handleException(FirebaseAuthException exception) {
final message = switch (exception.code) {
'invalid-email' => 'The email address is not valid.',
'user-disabled' =>
'The user corresponding to the given email has been disabled.',
'user-not-found' =>
'The user corresponding to the given email does not exist.',
'wrong-password' =>
'The password is invalid for the given email, or the account corresponding to the email does not have a password set.',
'email-already-in-use' =>
'The email address is already in use by another account.',
'operation-not-allowed' =>
'Indicates that Email & Password accounts are not enabled.',
'weak-password' => 'The password must be 6 characters long or more.',
(_) => 'An undefined Error happened.'
};
AppSnackbar.show(message);
}
}
116 changes: 56 additions & 60 deletions lib/features/session/ui/auth_page.dart
Original file line number Diff line number Diff line change
@@ -1,81 +1,77 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:varanasi_mobile_app/features/session/cubit/session_cubit.dart';
import 'package:varanasi_mobile_app/gen/assets.gen.dart';
import 'package:varanasi_mobile_app/utils/extensions/extensions.dart';
import 'package:varanasi_mobile_app/utils/helpers/ressponsive_sizer.dart';
import 'package:varanasi_mobile_app/utils/routes.dart';

class AuthPage extends StatelessWidget {
const AuthPage({super.key});

@override
Widget build(BuildContext context) {
return AnnotatedRegion(
value: const SystemUiOverlayStyle(statusBarBrightness: Brightness.dark),
child: Scaffold(
body: SafeArea(
child: Align(
alignment: Alignment.center,
child: SizedBox(
width: Device.width * 0.8,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Assets.icon.appIconMonotone.svg(
placeholderBuilder: (ctx) =>
const SizedBox(width: 48, height: 48),
return Scaffold(
body: SafeArea(
child: Align(
alignment: Alignment.center,
child: SizedBox(
width: Device.width * 0.8,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Assets.icon.appIconMonotone.svg(
placeholderBuilder: (ctx) =>
const SizedBox(width: 48, height: 48),
),
const SizedBox(height: 20),
Text(
"Millions of Songs.\nForever Free.",
style: context.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
const SizedBox(height: 20),
Text(
"Millions of Songs.\nForever Free.",
style: context.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
textAlign: TextAlign.center,
),
const SizedBox(height: 36),
FilledButton.tonal(
style: _buildButtonStyles(),
onPressed: () => context.pushNamed(AppRoutes.signup.name),
child: _buildText(context, "Sign up for free"),
),
const SizedBox(height: 8),
OutlinedButton.icon(
icon: Assets.icon.google.svg(width: 24, height: 24),
onPressed: context.read<SessionCubit>().continueWithGoogle,
style: _buildButtonStyles(),
label: Center(
child: _buildText(context, "Continue with Google"),
),
const SizedBox(height: 36),
FilledButton.tonal(
onPressed: () {},
child: const Text(
"Sign up for free",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
OutlinedButton.icon(
icon: const Icon(Icons.facebook),
onPressed: context.read<SessionCubit>().continueWithGoogle,
style: OutlinedButton.styleFrom(
foregroundColor: context.colorScheme.onBackground,
),
label: const Center(
child: Text(
"Continue with Google",
style: TextStyle(
fontWeight: FontWeight.w800,
),
),
),
),
TextButton(
style: TextButton.styleFrom(
foregroundColor: context.colorScheme.onBackground,
),
onPressed: () {},
child: const Text(
"Log in",
style: TextStyle(fontWeight: FontWeight.bold),
),
)
],
),
),
const SizedBox(height: 8),
TextButton(
onPressed: () => context.pushNamed(AppRoutes.login.name),
child: _buildText(context, "Log in"),
)
],
),
),
),
),
);
}

ButtonStyle _buildButtonStyles() {
return OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
);
}

Text _buildText(BuildContext context, String text) {
return Text(
text,
style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold),
);
}
}
Loading

0 comments on commit a5011c5

Please sign in to comment.