Skip to content

Commit

Permalink
Merge pull request #26 from SWM-M3PRO/feature/M3-181-logoutAndRefresh
Browse files Browse the repository at this point in the history
M3-181 로그아웃 및 토큰 리프레시 구현
  • Loading branch information
koomin1227 authored Jul 12, 2024
2 parents 256a2d5 + d978ff0 commit f48c54f
Show file tree
Hide file tree
Showing 16 changed files with 248 additions and 30 deletions.
2 changes: 1 addition & 1 deletion lib/controllers/login_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class LoginController extends GetxController {

loginWithKakao() async {
try {
AuthResponse authResponse = await authService.loginWithKakao();
LoginResponse authResponse = await authService.loginWithKakao();
if (authResponse.isSignUp!) {
Get.toNamed('/signup');
} else {
Expand Down
4 changes: 2 additions & 2 deletions lib/controllers/my_page_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class MyPageController extends GetxController {
return currentUserInfo.value.communityName ?? "-";
}

int getCurrentUserPixel(){
int getCurrentUserPixel() {
return userPixelCount.value.currentPixelCount ?? 0;
}

int getAccumulateUserPixel(){
int getAccumulateUserPixel() {
return userPixelCount.value.accumulatePixelCount ?? 0;
}
}
36 changes: 36 additions & 0 deletions lib/controllers/setting_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../service/auth_service.dart';

class SettingController extends GetxController {
final AuthService authService = AuthService();

logout() async {
_showLogoutDialog();
}

void _showLogoutDialog() {
Get.dialog(
AlertDialog(
title: Text('로그아웃 하시겠습니까?'),
actions: [
TextButton(
child: Text('아니오'),
onPressed: () async {
await authService.logout();
Get.back();
},
),
TextButton(
child: Text('예'),
onPressed: () async {
await authService.logout();
Get.offAllNamed('/login');
},
),
],
),
);
}
}
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:kakao_flutter_sdk/kakao_flutter_sdk_common.dart';

import 'screens/login_screen.dart';
import 'screens/main_screen.dart';
import 'screens/setting_screen.dart';
import 'service/auth_service.dart';

Future<void> main() async {
Expand Down Expand Up @@ -41,6 +42,7 @@ class MyApp extends StatelessWidget {
getPages: [
GetPage(name: '/main', page: () => const MainScreen()),
GetPage(name: '/login', page: () => const LoginScreen()),
GetPage(name: '/setting', page: () => const SettingScreen()),
],
);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/models/auth_response.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
class AuthResponse {
class LoginResponse {
String? accessToken;
String? refreshToken;
bool? isSignUp;

AuthResponse({
LoginResponse({
this.accessToken,
this.refreshToken,
this.isSignUp,
});

AuthResponse.fromJson(Map<String, dynamic> json) {
LoginResponse.fromJson(Map<String, dynamic> json) {
accessToken = json['accessToken'];
refreshToken = json['refreshToken'];
isSignUp = json['isSignUp'];
Expand Down
14 changes: 14 additions & 0 deletions lib/models/reissue_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class ReissueResponse {
String? accessToken;
String? refreshToken;

ReissueResponse({
this.accessToken,
this.refreshToken,
});

ReissueResponse.fromJson(Map<String, dynamic> json) {
accessToken = json['accessToken'];
refreshToken = json['refreshToken'];
}
}
2 changes: 0 additions & 2 deletions lib/screens/search_group.dart

This file was deleted.

57 changes: 57 additions & 0 deletions lib/screens/setting_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import '../controllers/setting_controller.dart';
import '../widgets/setting/setting_item.dart';
import '../widgets/setting/setting_section.dart';

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

@override
Widget build(BuildContext context) {
SettingController settingController = Get.put(SettingController());

return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('설정'),
),
body: ListView(
children: [
SettingsSection(
title: '설정',
items: [
SettingsItem(title: '수령 정보 설정'),
SettingsItem(title: '알림 설정'),
SettingsItem(title: '맵 설정'),
SettingsItem(title: '실험실'),
SettingsItem(title: '앱 최적화'),
SettingsItem(title: 'App Store 리뷰 남기기'),
],
),
SettingsSection(
title: '가이드',
items: [
SettingsItem(title: '공지사항'),
SettingsItem(title: '플레이 가이드'),
SettingsItem(title: '고객 문의 및 개선 요청'),
],
),
SettingsSection(
title: '기타',
items: [
SettingsItem(title: '서비스이용약관'),
SettingsItem(
title: '로그아웃',
onTap: () {
settingController.logout();
},
),
],
),
],
),
);
}
}
26 changes: 18 additions & 8 deletions lib/service/auth_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,20 @@ class AuthService {
return _instance;
}

logout() {
secureStorage.deleteAccessToken();
secureStorage.deleteRefreshToken();
UserManager().init();
logout() async {
try {
await postLogout();
} catch (e) {
debugPrint('Error during logout: $e');
} finally {
await secureStorage.deleteAccessToken();
await secureStorage.deleteRefreshToken();
UserManager().init();
}
}

postLogout() async {
await dio.post('/auth/logout');
}

Future<bool> isLogin() async {
Expand All @@ -41,22 +51,22 @@ class AuthService {

loginWithKakao() async {
String accessToken = await _getKakaoAccessToken();
AuthResponse authResponse = await postKakaoLogin(accessToken);
LoginResponse authResponse = await postKakaoLogin(accessToken);
await _saveTokens(authResponse);
return authResponse;
}

Future<void> _saveTokens(AuthResponse authResponse) async {
Future<void> _saveTokens(LoginResponse authResponse) async {
await secureStorage.writeAccessToken(authResponse.accessToken);
await secureStorage.writeRefreshToken(authResponse.refreshToken);
UserManager().setUserId(_extractUserIdFromToken(authResponse.accessToken!));
}

Future<AuthResponse> postKakaoLogin(String accessToken) async {
Future<LoginResponse> postKakaoLogin(String accessToken) async {
try {
var response = await dio
.post('/auth/kakao/login', data: {"accessToken": accessToken});
return AuthResponse.fromJson(response.data["data"]);
return LoginResponse.fromJson(response.data["data"]);
} catch (error) {
throw Exception("로그인 실패");
}
Expand Down
47 changes: 42 additions & 5 deletions lib/utils/dio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get/get.dart' hide Response;

import '../models/reissue_response.dart';
import '../service/auth_service.dart';
import 'secure_storage.dart';

Expand Down Expand Up @@ -42,18 +43,54 @@ class DioService {
onError: (
DioException dioException,
ErrorInterceptorHandler errorInterceptorHandler,
) {
) async {
if (dioException.response?.statusCode == HttpStatus.unauthorized) {
AuthService().logout();
Get.offAllNamed('/login');
try {
ReissueResponse reissueResponse = await reissueToken();
await secureStorage.writeAccessToken(reissueResponse.accessToken);
await secureStorage
.writeRefreshToken(reissueResponse.refreshToken);

Response<dynamic> resendResponse =
await resendRequest(dioException, reissueResponse);
errorInterceptorHandler.resolve(resendResponse);
debugPrint('-------------refresh---------------');
} catch (err) {
AuthService().logout();
Get.offAllNamed('/login');
}
} else {
logError(dioException);
return errorInterceptorHandler.next(dioException);
}
logError(dioException);
return errorInterceptorHandler.next(dioException);
},
),
);
}

Future<Response<dynamic>> resendRequest(
DioException dioException,
ReissueResponse reissueResponse,
) async {
final options = dioException.requestOptions;
final dio = Dio();
options.headers.addAll({
'Authorization': 'Bearer ${reissueResponse.accessToken}',
});
var response = await dio.fetch(options);
return response;
}

Future<ReissueResponse> reissueToken() async {
final refreshToken = await secureStorage.readRefreshToken();
final dio = Dio();
var response = await dio
.post("$baseUrl/auth/reissue", data: {"refreshToken": refreshToken});
ReissueResponse reissueResponse =
ReissueResponse.fromJson(response.data["data"]);
return reissueResponse;
}

Dio getDio() {
return _dio;
}
Expand Down
9 changes: 9 additions & 0 deletions lib/widgets/common/app_bar.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class MapAppBar extends StatelessWidget {
const MapAppBar({super.key});
Expand Down Expand Up @@ -44,6 +45,14 @@ class MyPageAppBar extends StatelessWidget {
return AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('마이페이지'),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
Get.toNamed('/setting');
},
),
],
);
}
}
2 changes: 1 addition & 1 deletion lib/widgets/map/pixel_owner_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class PixelOwnerInfo extends StatelessWidget {
fit: BoxFit.cover,
)
: Image.asset(
'assets/default_profile_image.png',
'assets/images/default_profile_image.png',
width: 50,
height: 50,
),
Expand Down
9 changes: 5 additions & 4 deletions lib/widgets/map/visited_user_list_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class VisitedUserListView extends StatelessWidget {
fit: BoxFit.cover,
)
: Image.asset(
'assets/default_profile_image.png',
'assets/images/default_profile_image.png',
width: 50,
height: 50,
),
Expand All @@ -47,9 +47,10 @@ class VisitedUserListView extends StatelessWidget {
Text(
visitList[index].nickname!,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,),
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
Expand Down
9 changes: 5 additions & 4 deletions lib/widgets/pixel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import '../models/individual_history_pixel.dart';
import '../models/individual_history_pixel_info.dart';
import '../models/individual_mode_pixel.dart';
import '../models/individual_mode_pixel_info.dart';
import '../utils/user_manager.dart';
import 'map/individual_history_pixel_info_bottom_sheet.dart';
import 'map/individual_mode_pixel_info_bottom_sheet.dart';

class Pixel extends Polygon {
static const double latPerPixel = 0.000724;
static const double lonPerPixel = 0.000909;

static const int defaultUserId = 2;

final int x;
final int y;
final int pixelId;
Expand Down Expand Up @@ -89,8 +88,10 @@ class Pixel extends Polygon {
strokeWidth: 1,
onTap: (int pixelId) async {
IndividualHistoryPixelInfo pixelInfo =
await Get.find<PixelInfoController>()
.getIndividualHistoryPixelInfo(pixelId, defaultUserId);
await Get.find<PixelInfoController>().getIndividualHistoryPixelInfo(
pixelId,
UserManager().getUserId()!,
);

Get.bottomSheet(
IndividualHistoryPixelInfoBottomSheet(pixelInfo: pixelInfo),
Expand Down
20 changes: 20 additions & 0 deletions lib/widgets/setting/setting_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';

class SettingsItem extends StatelessWidget {
final String title;
final VoidCallback? onTap;

const SettingsItem({required this.title, this.onTap, super.key});

@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
title,
style: const TextStyle(color: Colors.black),
),
trailing: const Icon(Icons.arrow_forward_ios, color: Colors.black),
onTap: onTap,
);
}
}
Loading

0 comments on commit f48c54f

Please sign in to comment.