Skip to content

Commit

Permalink
feat: add login and sign up poc (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jiseeeh authored Feb 10, 2024
1 parent 1ecec6a commit a988a99
Show file tree
Hide file tree
Showing 30 changed files with 1,125 additions and 85 deletions.
32 changes: 32 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change.

Fixes # (issue)

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

- [ ] Manual Testing
- [ ] Test B

# Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
21 changes: 19 additions & 2 deletions android/app/google-services.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,32 @@
"package_name": "com.example.u_do_note"
}
},
"oauth_client": [],
"oauth_client": [
{
"client_id": "504837693421-vlng7bdqnp26g9806fgke4hlj8ajuikp.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCkVzqgGRFouB2-h5VMjnbWM939GNIKqw8"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
"other_platform_oauth_client": [
{
"client_id": "504837693421-vlng7bdqnp26g9806fgke4hlj8ajuikp.apps.googleusercontent.com",
"client_type": 3
},
{
"client_id": "504837693421-5s2665iv83kjir2gbac4hkilrs0nl290.apps.googleusercontent.com",
"client_type": 2,
"ios_info": {
"bundle_id": "com.example.uDoNote"
}
}
]
}
}
}
Expand Down
40 changes: 23 additions & 17 deletions dev_readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

- Add these folders under your new feature folder.

> 💡 If you are not going to use the folder, just leave a `.gitkeep` file inside.
> 💡 If you are not going to use the folder, just leave a `.gitkeep` file inside if you think you might need it in the future.
>
>💡file names must be in **snake_case.**
```txt
📦reservation
Expand All @@ -30,19 +32,23 @@
┃ ┗ 📂widgets
```

## Extra Notes

1. file names must be in **snake_case.**
2. You can start adding your needed entities in the domain layer.
3. Then define the feature's methods in the domain layer's repositories just follow the name as `<FeatureName>Repository`.
4. After defining the methods, you can start to work on the use cases, every use case must have a `call` method.
5. And should be composed of the repository (composition).
6. Repeat **4** until you added all of the use cases you need.
7. Next is work on the data layer, make the models.
8. The properties of the models is the same as the entity but it haves a method for converting a model from entity or vice versa.
9. Add hive annotations to your models.
10. After adding hive annotations, build the packages `dart run`
11. Setup data sources
12. Implement the repositories in the data layer using the data sources (using composition)

- ⚡ Start coding! ⚡$$
## Next Steps

1. You can start adding your needed entities in the domain layer.
2. Then define the feature's methods in the domain layer's repositories just follow the name as `<FeatureName>Repository`.
3. After defining the methods, you can start to work on the use cases, every use case must have a `call` method.
4. And should be composed of the repository (composition).
5. Repeat **4** until you added all of the use cases you need.
6. Next is work on the data layer, make the models.
7. The properties of the models is the same as the entity but it haves a method for converting a model from entity or vice versa.
8. Add hive annotations to your models.
9. After adding hive annotations, build the packages `dart run build_runner build`
10. Setup data sources
11. Implement the repositories in the data layer using the data sources (using composition)
12. Add providers for every use case, and then run `dart run build_runner build` to generate the part files
13. Next is start working with the ui, place them under `pages/` and should be named as `<page_name>_screen.dart`
14. Add that route under in `app_route.dart` and then build again `dart run build_runner build`

> 💡 If you do not want to keep building, use watch instead `dart run build_runner watch`
## Extra information
7 changes: 7 additions & 0 deletions lib/core/error/failures.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@ abstract class Failure {

Failure(this.message);
}


class AuthenticationException extends Failure {
final String code;

AuthenticationException({required this.code, required String message}) : super(message);
}
32 changes: 32 additions & 0 deletions lib/core/logger/logger.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:logger/logger.dart';

var logger = Logger(
printer: PrettyPrinter(
methodCount: 2, // Number of method calls to be displayed
errorMethodCount: 8, // Number of method calls if stacktrace is provided
lineLength: 120, // Width of the output
colors: true, // Colorful log messages
printEmojis: true, // Print an emoji for each log message
printTime: false // Should each log print contain a timestamp
),
);

void logWarningOnly() {
Logger.level = Level.warning;
}

/*
Samples
logger.t("Trace log");
logger.d("Debug log");
logger.i("Info log");
logger.w("Warning log");
logger.e("Error log", error: 'Test Error');
logger.f("What a fatal log", error: error, stackTrace: stackTrace);
*/
12 changes: 12 additions & 0 deletions lib/core/shared/widgets/snackbar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';

// TODO: can be improved or just use a package for snackbars

SnackBar createSnackbar(String message) {
return SnackBar(
content: Text(
message,
textAlign: TextAlign.center,
),
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'package:firebase_auth/firebase_auth.dart';

import 'package:u_do_note/features/authentication/data/models/user_model.dart';
import 'package:u_do_note/core/logger/logger.dart';

class UserRemoteDataSource {
final FirebaseAuth _auth;

UserRemoteDataSource(this._auth);

Future<UserModel> signInWithEmailAndPassword(
String email, String password) async {
final userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);

logger.i(
"Signing in with email and password: \n email: $email \n password: $password");

return UserModel.fromFirebaseUser(userCredential.user);
}

Future<UserModel> signUpWithEmailAndPassword(
String email, String password) async {
final userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);

// TODO: create user document with the created user's id.

logger.i(
"Signing up with email and password: \n email: $email \n password: $password");

return UserModel.fromFirebaseUser(userCredential.user);
}
}
32 changes: 16 additions & 16 deletions lib/features/authentication/data/models/user_model.dart
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:u_do_note/features/authentication/domain/entities/user.dart';

typedef FirebaseUser = User;

class UserModel {
final String id;
final String email;
final String name;
final String photoUrl;
final String uid;

UserModel({
required this.id,
required this.email,
required this.name,
required this.photoUrl,
required this.uid,
});

// entity to model
factory UserModel.fromEntity(User user) => UserModel(
id: user.id,
email: user.email,
name: user.name,
photoUrl: user.photoUrl,
uid: user.uid);
factory UserModel.fromEntity(UserEntity user) => UserModel(
id: user.id,
email: user.email,
name: user.name,
photoUrl: user.photoUrl,
);

// model to entity
User toEntity() =>
User(id: id, email: email, name: name, photoUrl: photoUrl, uid: uid);
UserEntity toEntity() =>
UserEntity(id: id, email: email, name: name, photoUrl: photoUrl);

// from firestore to model
factory UserModel.fromSnapshot(Map<String, dynamic> snapshot) {
factory UserModel.fromFirebaseUser(FirebaseUser? firebaseUser) {
return UserModel(
id: snapshot['id'],
email: snapshot['email'],
name: snapshot['name'],
photoUrl: snapshot['photoUrl'],
uid: snapshot['uid'],
id: firebaseUser!.uid,
email: firebaseUser.email!,
name: firebaseUser.displayName ?? '',
photoUrl: firebaseUser.photoURL ?? '',
);
}
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,38 @@
import 'package:dartz/dartz.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'package:u_do_note/core/error/failures.dart';
import 'package:u_do_note/features/authentication/domain/entities/user.dart';
import 'package:u_do_note/features/authentication/data/datasources/user_remote_datasource.dart';
import 'package:u_do_note/features/authentication/data/models/user_model.dart';
import 'package:u_do_note/features/authentication/domain/repositories/user_repository.dart';

class UserRepositoryImpl implements UserRepository {

// Data sources
final UserRemoteDataSource userRemoteDataSource;

// 32:10 youtube
@override
Future<Either<Failure, User>> signInWithEmailAndPassword({required String email, required String password}) {
// TODO: implement signInWithEmailAndPassword
throw UnimplementedError();
}

@override
Future<Either<Failure, User>> signInWithFacebook() {
// TODO: implement signInWithFacebook
throw UnimplementedError();
}

@override
Future<Either<Failure, User>> signInWithGoogle() {
// TODO: implement signInWithGoogle
throw UnimplementedError();
}
UserRepositoryImpl(this.userRemoteDataSource);

@override
Future<Either<Failure, User>> signOut() {
// TODO: implement signOut
throw UnimplementedError();
Future<Either<Failure, UserModel>> signUpWithEmailAndPassword(
{required String email, required String password}) async {
try {
final userModel = await userRemoteDataSource.signUpWithEmailAndPassword(
email, password);
return Right(userModel);
} on FirebaseAuthException catch (e) {
return Left(AuthenticationException(code: e.code, message: e.message!));
}
}

@override
Future<Either<Failure, User>> signUpWithEmailAndPassword({required String email, required String password}) {
// TODO: implement signUpWithEmailAndPassword
throw UnimplementedError();
Future<Either<Failure, UserModel>> signInWithEmailAndPassword(
{required String email, required String password}) async {
try {
final userModel = await userRemoteDataSource.signInWithEmailAndPassword(
email, password);
return Right(userModel);
} on FirebaseAuthException catch (e) {
return Left(AuthenticationException(code: e.code, message: e.message!));
}
}

}
}
6 changes: 2 additions & 4 deletions lib/features/authentication/domain/entities/user.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
class User {
class UserEntity {
final String id;
final String email;
final String name;
final String photoUrl;
final String uid;

User({
UserEntity({
required this.id,
required this.email,
required this.name,
required this.photoUrl,
required this.uid,
});
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import 'package:dartz/dartz.dart';
import 'package:u_do_note/core/error/failures.dart';
import 'package:u_do_note/features/authentication/domain/entities/user.dart';
import 'package:u_do_note/features/authentication/data/models/user_model.dart';

abstract class UserRepository {
Future<Either<Failure, User>> signInWithEmailAndPassword(
Future<Either<Failure, UserModel>> signInWithEmailAndPassword(
{required String email, required String password});
Future<Either<Failure, User>> signUpWithEmailAndPassword(
Future<Either<Failure, UserModel>> signUpWithEmailAndPassword(
{required String email, required String password});
Future<Either<Failure, User>> signInWithGoogle();
Future<Either<Failure, User>> signInWithFacebook();
Future<Either<Failure, User>> signOut();
// Future<Either<Failure, User>> getCurrentUser();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:dartz/dartz.dart';

import 'package:u_do_note/core/error/failures.dart';
import 'package:u_do_note/features/authentication/data/models/user_model.dart';
import 'package:u_do_note/features/authentication/domain/repositories/user_repository.dart';

class SignInWithEmailAndPassword {
final UserRepository _userRepository;

SignInWithEmailAndPassword(this._userRepository);

Future<Either<Failure, UserModel>> call(String email, String password) {
return _userRepository.signInWithEmailAndPassword(
email: email, password: password);
}
}
Loading

0 comments on commit a988a99

Please sign in to comment.