import 'package:firebase_auth/firebase_auth.dart'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.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/business_logic/models/req/sc_user_profile_cmd.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.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'; import '../../shared/data_sources/models/enum/sc_erro_code.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 { final appleCredential = await SignInWithApple.getAppleIDCredential( // 审核要求 Apple 登录不能依赖姓名或邮箱授权。 scopes: const [], ); uid = appleCredential.userIdentifier ?? ""; SCLoadingManager.show(); if (uid.isNotEmpty) { await DataPersistence.setPendingChannelAuth(authType, uid); final user = await _loginOrRegisterApple(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 _loginOrRegisterApple( String authType, String openId, ) async { try { return await SCAccountRepository().loginForChannel( authType, openId, suppressRegistrationRedirect: true, ); } on DioException catch (error) { if (!_isUserNotRegistered(error)) { rethrow; } // Apple 首登自动建立最小资料:昵称由服务端随机生成,未向 Apple 请求姓名或邮箱。 final defaultBirthday = DateTime(2000, 1, 1); final defaultProfile = SCUserProfileCmd( userNickname: '', userAvatar: '', userSex: 0, bornYear: defaultBirthday.year, bornMonth: defaultBirthday.month, bornDay: defaultBirthday.day, age: DateTime.now().year - defaultBirthday.year, countryCode: 'US', countryName: 'United States', ); return SCAccountRepository().regist(authType, openId, defaultProfile); } } bool _isUserNotRegistered(DioException error) { final data = error.response?.data; if (data is! Map) { return false; } final rawCode = data['errorCode'] ?? data['code']; final code = rawCode is num ? rawCode.toInt() : int.tryParse(rawCode?.toString() ?? ''); return code == SCErroCode.userNotRegistered.code; } Future logoutUser() async { await SCGoogleAuthService.signOut(); await DataPersistence.clearPendingChannelAuth(); _user = null; notifyListeners(); } void _showAuthError(Object error) { if (error is DioException) { return; } final message = error.toString().replaceFirst("Exception: ", "").trim(); if (message.isNotEmpty) { SCTts.show(message); } } }