101 lines
3.6 KiB
Dart
101 lines
3.6 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:google_sign_in/google_sign_in.dart';
|
||
|
|
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||
|
|
import 'identity_service.dart';
|
||
|
|
import 'puzzle_service.dart';
|
||
|
|
|
||
|
|
class SessionNotifier with ChangeNotifier {
|
||
|
|
final IdentityService _identityService = IdentityService();
|
||
|
|
final PuzzleService _puzzleService = PuzzleService();
|
||
|
|
|
||
|
|
UserSession? _session;
|
||
|
|
|
||
|
|
UserSession? get session => _session;
|
||
|
|
bool get isLoading => _session == null;
|
||
|
|
bool get isGuest => _session?.isGuest ?? true;
|
||
|
|
|
||
|
|
// 🔽 [수정] 'GoogleSignIn()' 생성자 대신 '.instance' 싱글톤 사용
|
||
|
|
final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
|
||
|
|
|
||
|
|
SessionNotifier() {
|
||
|
|
loadSession();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 앱 시작 시 저장된 세션 로드
|
||
|
|
Future<void> loadSession() async {
|
||
|
|
_session = await _identityService.getUserSession();
|
||
|
|
notifyListeners();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// (백엔드 연동 후) 로그인/계정 연결
|
||
|
|
Future<void> login(String provider) async {
|
||
|
|
if (isLoading) return;
|
||
|
|
|
||
|
|
final guestUserId = _session!.userId; // 현재 게스트 ID
|
||
|
|
String? idToken;
|
||
|
|
String? email;
|
||
|
|
String? userName;
|
||
|
|
|
||
|
|
try {
|
||
|
|
if (provider == 'google') {
|
||
|
|
// 🔽 [수정] 'signIn()' 메서드 대신 'authenticate()' 사용
|
||
|
|
final GoogleSignInAccount? googleUser = await _googleSignIn.authenticate();
|
||
|
|
if (googleUser == null) return; // 유저가 취소
|
||
|
|
|
||
|
|
final GoogleSignInAuthentication googleAuth = googleUser.authentication;
|
||
|
|
idToken = googleAuth.idToken; // 👈 [핵심] 이 토큰을 백엔드로 전송
|
||
|
|
email = googleUser.email;
|
||
|
|
userName = googleUser.displayName;
|
||
|
|
|
||
|
|
} else if (provider == 'apple') {
|
||
|
|
final credential = await SignInWithApple.getAppleIDCredential(
|
||
|
|
scopes: [ AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName ],
|
||
|
|
);
|
||
|
|
|
||
|
|
idToken = credential.identityToken; // 👈 [핵심] 이 토큰을 백엔드로 전송
|
||
|
|
email = credential.email;
|
||
|
|
userName = "${credential.givenName ?? ''} ${credential.familyName ?? ''}".trim();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (idToken == null) {
|
||
|
|
throw Exception("$provider 로그인에 실패했습니다.");
|
||
|
|
}
|
||
|
|
|
||
|
|
// [TODO] 백엔드에 'mergeAccount(guestUserId, idToken, provider)' API 호출
|
||
|
|
// 백엔드는 이 idToken을 검증하고, guestUserId의 데이터를
|
||
|
|
// 소셜 계정의 마스터 ID로 병합(merge)해야 합니다.
|
||
|
|
|
||
|
|
// --- 백엔드 응답 (임시 시뮬레이션) ---
|
||
|
|
// final backendResponse = await _puzzleService.mergeAccount(guestUserId, idToken, provider);
|
||
|
|
// _session = await _identityService.saveSocialLogin(
|
||
|
|
// newUserId: backendResponse.userId,
|
||
|
|
// newUserName: backendResponse.userName,
|
||
|
|
// newEmail: backendResponse.email,
|
||
|
|
// provider: provider
|
||
|
|
// );
|
||
|
|
|
||
|
|
// [임시] 백엔드 없으므로, 클라이언트 정보로 강제 저장 (테스트용)
|
||
|
|
_session = await _identityService.saveSocialLogin(
|
||
|
|
newUserId: "master-id-${email ?? provider}", // (임시)
|
||
|
|
newUserName: userName ?? "Social User",
|
||
|
|
newEmail: email ?? "No Email",
|
||
|
|
provider: provider
|
||
|
|
);
|
||
|
|
// --- 임시 시뮬레이션 끝 ---
|
||
|
|
|
||
|
|
notifyListeners();
|
||
|
|
|
||
|
|
} catch (e) {
|
||
|
|
debugPrint("$provider 로그인 오류: $e");
|
||
|
|
// [TODO] 유저에게 "로그인에 실패했습니다." 스낵바 표시
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// 로그아웃
|
||
|
|
Future<void> logout() async {
|
||
|
|
await _googleSignIn.signOut();
|
||
|
|
|
||
|
|
_session = await _identityService.logout();
|
||
|
|
notifyListeners();
|
||
|
|
}
|
||
|
|
}
|