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

Feat: [스플래시] 스플래시 유저 인증정보 핸들링 로직 구현 #29 #41

Merged
Merged
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
3 changes: 2 additions & 1 deletion lib/api/user_api.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:dio/dio.dart';
import 'package:dio/dio.dart' hide Headers;
import 'package:pets_next_door_flutter/features/user/data/models/user_data_model.dart';
import 'package:retrofit/retrofit.dart';

Expand All @@ -9,5 +9,6 @@ abstract class UserAPI {
factory UserAPI(Dio dio, {String baseUrl}) = _UserAPI;

@GET("/users/me")
@Headers({"requiresToken": true})
Future<UserDataModel> getUserData();
}
3 changes: 2 additions & 1 deletion lib/api/user_api.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/app/di/app_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ final class AppBinder {

/// 'Splash' 단계에서 우선적으로 Binding 해야되는 모듈들은
/// 아래 메소드에서 처리합
static void _initTopPriority() {}
static Future<void> _initTopPriority() async {}

static void init() {
_initTopPriority();
Expand Down
18 changes: 16 additions & 2 deletions lib/app/di/modules/user_di.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import 'package:get_it/get_it.dart';
import 'package:pets_next_door_flutter/app/di/feature_di_interface.dart';
import 'package:pets_next_door_flutter/features/user/data/local/user_local_data_source.dart';
import 'package:pets_next_door_flutter/features/user/data/local/user_local_data_source_impl.dart';
import 'package:pets_next_door_flutter/features/user/data/remote/user_remote_data_source_impl.dart';
import 'package:pets_next_door_flutter/features/user/repositories/user_repository_impl.dart';
import 'package:pets_next_door_flutter/features/user/user.dart';

final class UserDependencyInjection extends FeatureDependencyInjection {
@override
void dataSources() {
Future<void> dataSources() async {
GetIt.I.registerLazySingleton<UserRemoteDataSource>(
UserRemoteDataSourceImpl.new,
);

GetIt.I.registerLazySingleton<UserLocalDataSource>(
UserLocalDataSourceImpl.new);
}

@override
void repositories() {
GetIt.I.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(
userRemoteDataSource,
userLocalDataSource,
),
);
}
Expand All @@ -25,7 +31,15 @@ final class UserDependencyInjection extends FeatureDependencyInjection {
void useCases() {
GetIt.I
..registerFactory<GetUserDataUseCase>(
() => GetUserDataUseCase(
() => GetUserDataUseCase(userRepository),
)
..registerFactory<GetUserTokenUseCase>(
() => GetUserTokenUseCase(
userRepository,
),
)
..registerFactory(
() => UpdateUserTokenLocalUseCase(
userRepository,
),
);
Expand Down
3 changes: 3 additions & 0 deletions lib/app/env/flavors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:pets_next_door_flutter/app/env/firebase_options_dev.dart'
as dev;
import 'package:pets_next_door_flutter/app/env/firebase_options_prod.dart'
as prod;
import 'package:pets_next_door_flutter/core/services/local_storage_service.dart';

enum BuildType {
development,
Expand Down Expand Up @@ -45,6 +46,8 @@ class Flavor {
javaScriptAppKey: dotenv.env['JAVASCRIPT_APP_KEY'],
);

await LocalStorageService.init();

// 앱 DI 실행
AppBinder.init();
}
Expand Down
16 changes: 13 additions & 3 deletions lib/app/router/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@ import 'package:pets_next_door_flutter/presentation/pages/pet/register_pet_page.
import 'package:pets_next_door_flutter/presentation/pages/pet/steps/breed_search_view.dart';
import 'package:pets_next_door_flutter/presentation/pages/sign_in/sign_in_view.dart';
import 'package:pets_next_door_flutter/presentation/pages/sign_up/phone_auth_view.dart';
import 'package:pets_next_door_flutter/presentation/pages/splash/splash_page.dart';
import 'package:pets_next_door_flutter/presentation/pages/user/user_profile_view.dart';
import 'package:pets_next_door_flutter/presentation/pages/user/user_view.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'app_router.g.dart';

// private navigators
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final rootNavigatorKey = GlobalKey<NavigatorState>();
final _homeNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'home');
final _gatherNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'gather');
final _chatNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'chat');
final _userNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'user');

enum AppRoute {
splash,
signIn,
phoneAuth,
home,
Expand All @@ -38,13 +40,21 @@ enum AppRoute {
// ignore: unsupported_provider_value
GoRouter goRouter(GoRouterRef ref) {
return GoRouter(
initialLocation: '/home',
navigatorKey: _rootNavigatorKey,
initialLocation: '/${AppRoute.splash}',
navigatorKey: rootNavigatorKey,
debugLogDiagnostics: true,
redirect: (context, state) {
return null;
},
routes: [
GoRoute(
path: '/${AppRoute.splash}',
name: AppRoute.splash.name,
pageBuilder: (context, state) => MaterialPage(
key: state.pageKey,
child: SplashPage(),
),
),
GoRoute(
path: '/signIn',
name: AppRoute.signIn.name,
Expand Down
2 changes: 1 addition & 1 deletion lib/app/router/app_router.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/core/network_handling/app_dio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ abstract class AppDio {

static Dio? _instance;

static Dio getInstance() => _instance ??= _AppDio();
static Dio get instance => _instance ??= _AppDio();
}

class _AppDio with DioMixin implements Dio {
Expand Down
26 changes: 26 additions & 0 deletions lib/core/network_handling/exceptions/custom_exception.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
sealed class CustomException implements Exception {
const CustomException(this.code, this.message);

final String code;
final String message;

@override
String toString() => '$code: $message';
}

class UnAuthorizedException extends CustomException {
const UnAuthorizedException() : super('000000', '유저 인증 정보를 불러올 수 없습니다.');
}

class AlreadyExistUserDataException extends CustomException {
const AlreadyExistUserDataException() : super('100001', '이미 유저 데이터가 존재합니다.');
}

class NoUserDataException extends CustomException {
const NoUserDataException() : super('100002', '유저 데이터가 존재하지않습니다.');
}

class LocalDataNotUpdatedException extends CustomException {
const LocalDataNotUpdatedException()
: super('100003', '로컬 데이터를 업데이트 할 수 없습니다.');
}
17 changes: 12 additions & 5 deletions lib/core/network_handling/interceptors/token_interceptor.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:pets_next_door_flutter/presentation/providers/user/user_auth_provider.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:pets_next_door_flutter/features/user/user.dart';

class TokenInterceptor implements Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final isUserAuthorized = ProviderContainer().read(isUserAuthorizedProvider);
Future<void> onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
final requiresToken = (options.headers['requiresToken'] ?? true) as bool;

if (!requiresToken) return handler.next(options);

final isUserAuthorized = (FirebaseAuth.instance.currentUser != null);

if (isUserAuthorized) {
final token = '';
final token = await getUserTokenUseCase.call();
options.headers['Authorization'] = 'Bearer $token';
}

return handler.next(options);
}

@override
Expand Down
100 changes: 100 additions & 0 deletions lib/core/services/local_storage_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import 'package:shared_preferences/shared_preferences.dart';

/**
* SharedPreferences로 로컬 스토리지 사용하는 서비스
* 반드시 init 후에 사용해야 함.
* */
final class LocalStorageService implements SharedPreferences {
static late final SharedPreferences _sharedPreference;

static SharedPreferences get instance => _sharedPreference;

static Future<SharedPreferences> init() async =>
_sharedPreference = await SharedPreferences.getInstance();

@override
Future<bool> clear() {
return _sharedPreference.clear();
}

@override
@Deprecated('deprecated 된 메소드')
Future<bool> commit() {
return _sharedPreference.commit();
}

@override
bool containsKey(String key) {
return _sharedPreference.containsKey(key);
}

@override
Object? get(String key) {
return _sharedPreference.get(key);
}

@override
bool? getBool(String key) {
return _sharedPreference.getBool(key);
}

@override
double? getDouble(String key) {
return _sharedPreference.getDouble(key);
}

@override
int? getInt(String key) {
return _sharedPreference.getInt(key);
}

@override
Set<String> getKeys() {
return _sharedPreference.getKeys();
}

@override
String? getString(String key) {
return _sharedPreference.getString(key);
}

@override
List<String>? getStringList(String key) {
return _sharedPreference.getStringList(key);
}

@override
Future<void> reload() {
return _sharedPreference.reload();
}

@override
Future<bool> remove(String key) {
return _sharedPreference.remove(key);
}

@override
Future<bool> setBool(String key, bool value) {
return _sharedPreference.setBool(key, value);
}

@override
Future<bool> setDouble(String key, double value) {
return _sharedPreference.setDouble(key, value);
}

@override
Future<bool> setInt(String key, int value) {
return _sharedPreference.setInt(key, value);
}

@override
Future<bool> setString(String key, String value) {
return _sharedPreference.setString(key, value);
}

@override
Future<bool> setStringList(String key, List<String> value) {
return _sharedPreference.setStringList(key, value);
}
}
18 changes: 18 additions & 0 deletions lib/core/services/toast_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:fluttertoast/fluttertoast.dart';
import 'package:pets_next_door_flutter/app/router/app_router.dart';
import 'package:pets_next_door_flutter/presentation/widgets/toast/app_toast.dart';

class ToastService {
ToastService._();

static final FToast _fToast = FToast()
..init(rootNavigatorKey.currentContext!);

static void show(CustomToast toast) {
_fToast
..removeQueuedCustomToasts()
..showToast(
child: toast,
);
}
}
2 changes: 1 addition & 1 deletion lib/features/auth/data/phone_auth_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ final phoneAuthRepositoryProvider = Provider<DioAuthRepository>((ref) {
);
return DioAuthRepository(
api: PNDAuthAPI(apiKey),
client: AppDio.getInstance(),
client: AppDio.instance,
);
});
2 changes: 1 addition & 1 deletion lib/features/pet/data/pet_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ final petRepositoryProvider = Provider<PetRepository>((ref) {

return PetRepository(
api: PNDPetAPI(apiBaseUrl),
client: AppDio.getInstance(),
client: AppDio.instance,
);
});

Expand Down
4 changes: 4 additions & 0 deletions lib/features/user/data/local/user_local_data_source.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
abstract interface class UserLocalDataSource {
String? getUserToken();
Future<bool> updateUserToken({required String? token});
}
18 changes: 18 additions & 0 deletions lib/features/user/data/local/user_local_data_source_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:pets_next_door_flutter/core/services/local_storage_service.dart';
import 'package:pets_next_door_flutter/features/user/data/local/user_local_data_source.dart';

final class UserLocalDataSourceImpl implements UserLocalDataSource {
final LocalStorageService _localStorage = LocalStorageService();

@override
String? getUserToken() {
return _localStorage.getString('token');
}

@override
Future<bool> updateUserToken({required String? token}) async {
if (token == null) return _localStorage.remove('token');

return _localStorage.setString('token', token);
}
}
2 changes: 1 addition & 1 deletion lib/features/user/data/remote/user_remote_data_source.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:pets_next_door_flutter/features/user/data/models/user_data_model.dart';

abstract interface class UserRemoteDataSource {
Future<UserDataModel?> getUserData(String uid);
Future<UserDataModel> getUserData();
}
Loading