Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Email login #233

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions app/lib/app/data/data.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
export 'repository/app_repositoty_impl.dart';
export 'repository/theme_repository_impl.dart';
export 'repository/auth_repositoty_impl.dart';
export 'source/app_local_data_source.dart';
export 'source/theme_local_data_source.dart';
export 'source/local/app_local_data_source_impl.dart';
export 'source/local/theme_local_data_source_impl.dart';
export 'source/local/auth_local_data_source_impl.dart';
export 'source/remote/auth_remote_data_source_impl.dart';
export 'source/auth_local_data_source.dart';
export 'source/auth_remote_data_source.dart';
export 'source/theme_local_data_source.dart';
export 'source/mock/auth_local_data_source_mock.dart';
export 'source/mock/auth_remote_data_source_mock.dart';
export 'source/mock/theme_local_data_source_mock.dart';
export 'models/user_model.dart';
export 'models/token_response.dart';
export 'models/user_data_response.dart';
38 changes: 38 additions & 0 deletions app/lib/app/data/repository/auth_repositoty_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,44 @@ final class AuthRepositoryImpl implements AuthRepository {
return localDataSource.init;
}

@override
Future<void> loginWithEmail(String email) async {
try {
await remoteDataSource.loginWithEmail(email);
} catch (e, s) {
MqCrashlytics.report(e, s);
log('signWithEmail: error: $e\n$s');
}
}

@override
Future<Either<UserEntity, Exception>> fetchSmsCode({
required String code,
required String languageCode,
required Gender gender,
}) async {
try {
final res = await remoteDataSource.fetchSmsCode(code: code, languageCode: languageCode, gender: gender);

return res.fold(
Left.new,
(r) {
final userEntity = UserEntity(
accessToken: r.accessToken,
username: r.username,
gender: r.gender,
localeCode: r.localeCode,
);
return Right(userEntity);
},
);
} catch (e, s) {
log('signWithemail: error: $e\n$s');
MqCrashlytics.report(e, s);
return Left(AuthenticationExc(message: e.toString()));
}
}

@override
Future<void> setUserData(UserEntity userEntity) async {
try {
Expand Down
57 changes: 7 additions & 50 deletions app/lib/app/data/source/auth_local_data_source.dart
Original file line number Diff line number Diff line change
@@ -1,53 +1,10 @@
import 'package:meta/meta.dart';
import 'package:mq_storage/mq_storage.dart';
import 'package:my_quran/app/app.dart';
import 'package:my_quran/constants/contants.dart';

@immutable
final class AuthLocalDataSource {
const AuthLocalDataSource(this.storage);

final PreferencesStorage storage;

UserEntity? get init {
final userToken = storage.readString(key: StorageKeys.tokenKey);
final userGender = storage.readString(key: StorageKeys.genderKey);
final username = storage.readString(key: StorageKeys.usernameKey);
final localeCode = storage.readString(key: StorageKeys.localeKey);
if (userToken == null && userGender == null && username == null) return null;
return UserEntity(
accessToken: userToken!,
username: username!,
gender: userGender == Gender.male.name ? Gender.male : Gender.female,
localeCode: localeCode ?? 'en',
);
}

String? getToken() => storage.readString(key: StorageKeys.tokenKey);

Future<void> saveUserData(UserEntity userEntity) async {
await Future.wait([
storage.writeString(key: StorageKeys.localeKey, value: userEntity.localeCode),
storage.writeString(key: StorageKeys.genderKey, value: userEntity.gender.name),
storage.writeString(key: StorageKeys.usernameKey, value: userEntity.username),
]);
}

Future<void> saveGender(Gender gender) {
return storage.writeString(
key: StorageKeys.genderKey,
value: gender.name,
);
}

Future<void> saveLocaleCode(String localeCode) {
return storage.writeString(
key: StorageKeys.localeKey,
value: localeCode,
);
}

Future<void> logoutLocal() async {
await storage.clear();
}
abstract class AuthLocalDataSource {
UserEntity? get init;
String? getToken();
Future<void> saveUserData(UserEntity userEntity);
Future<void> saveGender(Gender gender);
Future<void> saveLocaleCode(String localeCode);
Future<void> logoutLocal();
}
143 changes: 12 additions & 131 deletions app/lib/app/data/source/auth_remote_data_source.dart
Original file line number Diff line number Diff line change
@@ -1,155 +1,36 @@
import 'package:meta/meta.dart';
import 'package:mq_storage/mq_storage.dart';
import 'package:my_quran/app/app.dart';
import 'package:my_quran/config/config.dart';
import 'package:my_quran/constants/contants.dart';
import 'package:my_quran/core/core.dart';

@immutable
final class AuthRemoteDataSource {
const AuthRemoteDataSource({
required this.client,
required this.storage,
required this.soccialAuth,
required this.isIntegrationTest,
});
abstract class AuthRemoteDataSource {
Future<void> loginWithEmail(String email);

final MqDio client;
final PreferencesStorage storage;
final SoccialAuth soccialAuth;
final bool isIntegrationTest;
Future<Either<UserModelResponse, Exception>> fetchSmsCode({
required String code,
required String languageCode,
required Gender gender,
});

Future<Either<UserModelResponse, Exception>> signInWithGoogle(
String languageCode,
Gender gender,
) async {
final googleAuth = await _getGoogleAuth();

final token = await client.postType(
apiConst.loginWithGoogle,
fromJson: TokenResponse.fromJson,
body: {'access_token': googleAuth.accessToken},
);

return token.fold(Left.new, (r) async {
final user = UserModelResponse(
accessToken: r.key,
username: googleAuth.name,
gender: gender,
localeCode: languageCode,
);

await storage.writeString(key: StorageKeys.tokenKey, value: user.accessToken);

return Right(user);
});
}

Future<_UserReqParam> _getGoogleAuth() async {
if (isIntegrationTest) {
return const _UserReqParam(
name: 'Test User',
accessToken: r'myquran_te$t_t0ken',
);
} else {
final googleAuth = await soccialAuth.signInWithGoogle();
final accessToken = googleAuth.credential?.accessToken ?? '';
final username = googleAuth.user?.displayName ?? '';
return _UserReqParam(
name: username,
accessToken: accessToken,
);
}
}
);

Future<Either<UserModelResponse, Exception>> signInWithApple(
String languageCode,
Gender gender,
) async {
final appleAuth = await _getAppleAuth();

final token = await client.postType(
apiConst.loginWithApple,
fromJson: TokenResponse.fromJson,
body: {'access_token': appleAuth.accessToken},
);
);

return token.fold(Left.new, (r) async {
final user = UserModelResponse(
accessToken: r.key,
username: appleAuth.name,
gender: gender,
localeCode: languageCode,
);

await storage.writeString(key: StorageKeys.tokenKey, value: user.accessToken);

return Right(user);
});
}

Future<_UserReqParam> _getAppleAuth() async {
if (isIntegrationTest) {
return const _UserReqParam(
name: 'Test User',
accessToken: r'myquran_te$t_t0ken',
);
} else {
final appleAuth = await soccialAuth.signInWithApple();
final accessToken = appleAuth.credential?.accessToken ?? '';
final username = appleAuth.user?.displayName ?? '';
return _UserReqParam(
name: username,
accessToken: accessToken,
);
}
}

Future<Either<UserDataResponse, Exception>> saveUserData(UserEntity userEntity) {
return client.putType(
apiConst.putProfile(userEntity.accessToken),
fromJson: UserDataResponse.fromJson,
body: {
'gender': userEntity.gender.name.toUpperCase(),
'language': userEntity.localeCode.toUpperCase(),
},
);
}
Future<Either<UserDataResponse, Exception>> saveUserData(UserEntity userEntity);

Future<Either<UserDataResponse, Exception>> pathGender({
required String userId,
required Gender gender,
}) {
return client.patchType(
apiConst.putProfile(userId),
fromJson: UserDataResponse.fromJson,
body: {'gender': gender.name.toUpperCase()},
);
}
});

Future<Either<UserDataResponse, Exception>> pathLocaleCode({
required String userId,
required String localeCode,
}) {
return client.patchType(
apiConst.putProfile(userId),
fromJson: UserDataResponse.fromJson,
body: {'language': localeCode.toUpperCase()},
);
}

Future<void> logoutRemote() async {
await soccialAuth.logOut();
}
}

@immutable
final class _UserReqParam {
const _UserReqParam({
required this.name,
required this.accessToken,
});

final String name;
final String accessToken;
Future<void> logoutRemote();
}
59 changes: 59 additions & 0 deletions app/lib/app/data/source/local/auth_local_data_source_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:meta/meta.dart';
import 'package:mq_storage/mq_storage.dart';
import 'package:my_quran/app/app.dart';
import 'package:my_quran/constants/contants.dart';

@immutable
final class AuthLocalDataSourceImpl implements AuthLocalDataSource {
const AuthLocalDataSourceImpl(this.storage);

final PreferencesStorage storage;

@override
UserEntity? get init {
final userToken = storage.readString(key: StorageKeys.tokenKey);
final userGender = storage.readString(key: StorageKeys.genderKey);
final username = storage.readString(key: StorageKeys.usernameKey);
final localeCode = storage.readString(key: StorageKeys.localeKey);
if (userToken == null && userGender == null && username == null) return null;
return UserEntity(
accessToken: userToken!,
username: username!,
gender: userGender == Gender.male.name ? Gender.male : Gender.female,
localeCode: localeCode ?? 'en',
);
}

@override
String? getToken() => storage.readString(key: StorageKeys.tokenKey);

@override
Future<void> saveUserData(UserEntity userEntity) async {
await Future.wait([
storage.writeString(key: StorageKeys.localeKey, value: userEntity.localeCode),
storage.writeString(key: StorageKeys.genderKey, value: userEntity.gender.name),
storage.writeString(key: StorageKeys.usernameKey, value: userEntity.username),
]);
}

@override
Future<void> saveGender(Gender gender) {
return storage.writeString(
key: StorageKeys.genderKey,
value: gender.name,
);
}

@override
Future<void> saveLocaleCode(String localeCode) {
return storage.writeString(
key: StorageKeys.localeKey,
value: localeCode,
);
}

@override
Future<void> logoutLocal() async {
await storage.clear();
}
}
34 changes: 34 additions & 0 deletions app/lib/app/data/source/local/theme_local_data_source_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:my_quran/app/app.dart';
import 'package:my_quran/constants/contants.dart';
import 'package:mq_storage/mq_storage.dart';
import 'package:my_quran/theme/theme.dart';

@immutable
final class ThemeLocalDataSourceImpl implements ThemeLocalDataSource {
const ThemeLocalDataSourceImpl(this.storage);
final PreferencesStorage storage;

@override
CustomTheme get initialTheme {
final isDark = storage.readBool(key: StorageKeys.modeKey);
final cachedColorIndex = storage.readInt(key: StorageKeys.colorKey);
final brightness = isDark != null ? (isDark ? Brightness.dark : Brightness.light) : Brightness.light;
final targetColor = _getColor(cachedColorIndex);
return CustomTheme(brightness, targetColor);
}

@override
Future<void> saveThemePrimaryColor(int index) async {
await storage.writeInt(key: StorageKeys.colorKey, value: index);
}

@override
Future<void> saveThemeMode({required bool isDark}) async {
await storage.writeBool(key: StorageKeys.modeKey, value: isDark);
}

Color _getColor(int? cacheColor) {
return TargetColor.fromIndex(cacheColor ?? 0).color;
}
}
Loading
Loading