가입 시 약관 동의 단계 추가 — 필수·선택 항목 구분 · 개인정보처리방침 고지 · 동의 기록 저장
This commit is contained in:
@@ -6,21 +6,85 @@ import '../../core/firebase/firebase_boot.dart';
|
||||
import 'auth.dart';
|
||||
import 'social_providers.dart';
|
||||
|
||||
/// 로그인 상태 보관소. 라우터가 이걸 듣고 로그인/셸을 가른다.
|
||||
/// 가입 약관 관문 — 로그인한 계정이 약관 동의를 마쳤는가(`users/{uid}.termsAgreedAt`).
|
||||
enum TermsGate { checking, required, done }
|
||||
|
||||
/// 로그인 상태 보관소. 라우터가 이걸 듣고 로그인/약관/셸을 가른다.
|
||||
class AuthRepository extends ChangeNotifier {
|
||||
AuthRepository({
|
||||
List<SocialAuthProvider>? providers,
|
||||
AuthSession? session,
|
||||
EmailAuthProvider? qaEmailProvider,
|
||||
bool? releaseMode,
|
||||
Future<bool> Function(String uid)? needsTerms,
|
||||
Future<void> Function(Set<String> agreed)? recordTerms,
|
||||
}) : providers = providers ?? _defaultProviders(),
|
||||
_qaEmail = qaEmailProvider ?? const EmailPasswordProvider(),
|
||||
_session = session,
|
||||
_needsTerms = needsTerms,
|
||||
_recordTerms = recordTerms,
|
||||
_account = session?.currentAccount,
|
||||
// 기본값은 실제 빌드 모드. 테스트에서만 주입해 릴리스 동작을 재현한다
|
||||
// (kReleaseMode 는 컴파일 상수라 테스트에서 뒤집을 수 없다).
|
||||
_releaseMode = releaseMode ?? (kReleaseMode || kProfileMode) {
|
||||
_sessionSubscription = session?.accountChanges.listen(_replaceAccount);
|
||||
final initial = _account;
|
||||
if (initial != null) unawaited(_checkTerms(initial));
|
||||
}
|
||||
|
||||
/// 이 계정이 약관 동의를 거쳐야 하는가. null 이면 판정하지 않는다(목 모드 · 테스트).
|
||||
///
|
||||
/// **「로그인」 버튼으로 처음 들어온 소셜 계정도 여기서 걸린다** — 가입/로그인 버튼이나
|
||||
/// 제공자(카카오 · LINE 커스텀 토큰 포함)와 무관하게, 서버에 동의 기록이 없으면 셸에 들어가지 못한다.
|
||||
final Future<bool> Function(String uid)? _needsTerms;
|
||||
|
||||
/// 동의 기록(`fnSignupConsent`). 필수 누락은 서버가 거부한다.
|
||||
final Future<void> Function(Set<String> agreed)? _recordTerms;
|
||||
|
||||
TermsGate _gate = TermsGate.done;
|
||||
TermsGate get terms => _gate;
|
||||
|
||||
/// 가입 모드에서 제공자 인증 **전에** 동의한 약관. 인증 뒤 동의가 필요한 계정이면
|
||||
/// 이 값으로 기록하고 약관 화면을 다시 띄우지 않는다.
|
||||
Set<String>? pendingConsent;
|
||||
|
||||
/// 약관 동의를 서버에 기록하고 셸로 보낸다. 실패하면 예외를 그대로 던진다(화면이 안내).
|
||||
Future<void> agreeTerms(Set<String> agreed) async {
|
||||
await _recordTerms?.call(agreed);
|
||||
_gate = TermsGate.done;
|
||||
pendingConsent = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _checkTerms(AuthAccount account) async {
|
||||
final check = _needsTerms;
|
||||
if (check == null || account.isMock) {
|
||||
_gate = TermsGate.done;
|
||||
return;
|
||||
}
|
||||
_gate = TermsGate.checking;
|
||||
bool required;
|
||||
try {
|
||||
required = await check(account.uid);
|
||||
} catch (e) {
|
||||
// 판정하지 못하면 약관부터 다시 받는다 — 동의 없는 계정이 셸로 새는 쪽이 더 나쁘다.
|
||||
debugPrint('[AuthRepository] terms check failed: ${e.runtimeType}');
|
||||
required = true;
|
||||
}
|
||||
if (_account?.uid != account.uid) return;
|
||||
final pending = pendingConsent;
|
||||
pendingConsent = null;
|
||||
if (required && pending != null) {
|
||||
try {
|
||||
await agreeTerms(pending);
|
||||
return;
|
||||
} catch (e) {
|
||||
debugPrint('[AuthRepository] pending consent failed: ${e.runtimeType}');
|
||||
}
|
||||
if (_account?.uid != account.uid) return;
|
||||
}
|
||||
_gate = required ? TermsGate.required : TermsGate.done;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
final bool _releaseMode;
|
||||
@@ -172,7 +236,14 @@ class AuthRepository extends ChangeNotifier {
|
||||
|
||||
void _replaceAccount(AuthAccount? next) {
|
||||
if (_sameAccount(_account, next)) return;
|
||||
final uidChanged = _account?.uid != next?.uid;
|
||||
_account = next;
|
||||
if (next == null) {
|
||||
_gate = TermsGate.done;
|
||||
pendingConsent = null;
|
||||
} else if (uidChanged) {
|
||||
unawaited(_checkTerms(next));
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user