import 'package:firebase_auth/firebase_auth.dart'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:yumi/app/routes/sc_routes.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/shared/tools/sc_google_auth_utils.dart'; import 'package:yumi/shared/tools/sc_loading_manager.dart'; import 'package:yumi/shared/tools/sc_version_utils.dart'; import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:provider/provider.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:yumi/services/audio/rtm_manager.dart'; import '../../shared/data_sources/models/enum/sc_auth_type.dart'; class SocialChatAuthenticationManager extends ChangeNotifier { String authType = ""; String uid = ""; User? _user; User? get user => _user; bool get isSignedIn => _user != null; SocialChatAuthenticationManager() { _initializeAuthentication(); } Future _initializeAuthentication() async { try { await SCGoogleAuthService.initialize(); await _verifyExistingSession(); } catch (_) { // Login buttons should remain usable even if silent Google restore fails. } } Future _verifyExistingSession() async { final user = await SCGoogleAuthService.signInSilently(); _user = user; if (user != null) { authType = SCAuthType.GOOGLE.name; uid = user.uid; await DataPersistence.setPendingChannelAuth(authType, uid); } notifyListeners(); } Future authenticateUser(BuildContext context, String authType) async { if (authType == SCAuthType.GOOGLE.name) { this.authType = authType; try { final signedInUser = await SCGoogleAuthService.signInWithGoogle(); if (signedInUser == null) { return; } _user = signedInUser; uid = _user!.uid; SCLoadingManager.show(); if (uid.isNotEmpty) { await DataPersistence.setPendingChannelAuth(authType, uid); var user = await SCAccountRepository().loginForChannel(authType, uid); if (!context.mounted) { SCLoadingManager.hide(); return; } AccountStorage().setCurrentUser(user); await DataPersistence.clearPendingChannelAuth(); if (!context.mounted) { SCLoadingManager.hide(); return; } final canContinue = await SCVersionUtils.checkReview( context: context, ); if (!context.mounted || !canContinue) { SCLoadingManager.hide(); return; } await Provider.of( context, listen: false, ).resetLocalRoomState( fallbackRtmProvider: Provider.of( context, listen: false, ), ); if (!context.mounted) { return; } SCLoadingManager.hide(); notifyListeners(); SCNavigatorUtils.push(context, SCRoutes.home, replace: true); } } catch (e) { SCLoadingManager.hide(); _showAuthError(e); } } else if (authType == SCAuthType.APPLE.name) { this.authType = authType; try { _authDebugLog('apple sign-in start'); final isAvailable = await SignInWithApple.isAvailable(); _authDebugLog('apple sign-in availability=$isAvailable'); if (!isAvailable) { SCTts.show("Sign in with Apple is not available on this device."); return; } final appleCredential = await SignInWithApple.getAppleIDCredential( scopes: [ AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName, // 请求姓名权限 ], ); uid = appleCredential.userIdentifier ?? ""; _authDebugLog( 'apple credential success userIdentifier=${_maskForDebug(uid)} ' 'hasAuthorizationCode=${appleCredential.authorizationCode.isNotEmpty} ' 'hasIdentityToken=${(appleCredential.identityToken ?? "").isNotEmpty} ' 'hasEmail=${(appleCredential.email ?? "").isNotEmpty}', ); if (uid.isEmpty) { SCTts.show("Apple authorization failed. Please try again."); return; } SCLoadingManager.show(); await DataPersistence.setPendingChannelAuth(authType, uid); var user = await SCAccountRepository().loginForChannel(authType, uid); if (!context.mounted) { SCLoadingManager.hide(); return; } AccountStorage().setCurrentUser(user); await DataPersistence.clearPendingChannelAuth(); if (!context.mounted) { SCLoadingManager.hide(); return; } final canContinue = await SCVersionUtils.checkReview(context: context); if (!context.mounted || !canContinue) { SCLoadingManager.hide(); return; } await Provider.of( context, listen: false, ).resetLocalRoomState( fallbackRtmProvider: Provider.of(context, listen: false), ); if (!context.mounted) { return; } SCLoadingManager.hide(); notifyListeners(); SCNavigatorUtils.push(context, SCRoutes.home, replace: true); } catch (e) { SCLoadingManager.hide(); _showAuthError(e); } } } Future logoutUser() async { await SCGoogleAuthService.signOut(); await DataPersistence.clearPendingChannelAuth(); _user = null; notifyListeners(); } void _showAuthError(Object error) { _authDebugLog('authenticateUser error: $error'); if (error is DioException) { return; } if (error is SignInWithAppleAuthorizationException) { _showAppleAuthorizationError(error); return; } if (error is SignInWithAppleNotSupportedException) { SCTts.show("Sign in with Apple is not available on this device."); return; } final message = error.toString().replaceFirst("Exception: ", "").trim(); if (message.isNotEmpty) { SCTts.show(message); } } void _showAppleAuthorizationError( SignInWithAppleAuthorizationException error, ) { if (error.code == AuthorizationErrorCode.canceled) { return; } if (error.code == AuthorizationErrorCode.unknown) { SCTts.show( "Apple sign-in is not available. Please check the app signing profile.", ); return; } SCTts.show("Apple authorization failed. Please try again."); } void _authDebugLog(String message) { if (kDebugMode) { debugPrint('[Auth][Apple] $message'); } } String _maskForDebug(String value) { if (value.isEmpty) { return ''; } if (value.length <= 8) { return '***'; } return '${value.substring(0, 4)}...${value.substring(value.length - 4)}'; } }