signInWithGoogle method

Future<bool> signInWithGoogle(
  1. String username,
  2. BuildContext context, [
  3. String? customClientId
])

Main YouTube authentication method following working implementation flow

Implementation

Future<bool> signInWithGoogle(String username, BuildContext context, [String? customClientId]) async {
  try {
    debugPrint('🚀 Starting YouTube native authentication for: $username');

    // Check if we have a valid client ID (not the demo one)
    if (customClientId == null || customClientId == '1030678346906-lovkuds2ouqmoc8eu5qpo98spa6edv4o.apps.googleusercontent.com') {
      debugPrint('⚠️ No custom Google client ID provided - YouTube integration unavailable');
      _showSetupRequiredDialog(context);
      return false;
    }

    // STEP 1: Initialize SDK with custom client ID
    final GoogleSignIn googleSignIn = _initializeGoogleSignIn(customClientId);

    // STEP 2: Check if already signed in (silent sign-in)
    GoogleSignInAccount? account = await googleSignIn.signInSilently();

    // STEP 3: If not signed in, prompt for sign-in
    if (account == null) {
      debugPrint('📱 Prompting user for Google Sign-In');
      account = await googleSignIn.signIn();
    }

    if (account == null) {
      debugPrint('❌ User cancelled Google Sign-In');
      _showErrorSnackBar(context, 'Sign-in was cancelled');
      return false;
    }

    debugPrint('✅ Google Sign-In successful: ${account.email}');

    // STEP 4: Get authentication tokens
    final GoogleSignInAuthentication auth = await account.authentication;

    // STEP 5: CRITICAL - Check for server auth code (refresh token)
    final String? serverAuthCode = account.serverAuthCode;
    if (serverAuthCode == null) {
      debugPrint('⚠️ No server auth code available - refresh may fail');
      _showErrorSnackBar(context, 'Authentication incomplete. Please try again.');
      return false;
    }

    debugPrint('🔑 Got tokens with refresh capability');

    // STEP 6: Fetch YouTube channel info
    String channelName = 'Unknown Channel';
    String? channelId;

    try {
      final channelResponse = await http.get(
        Uri.parse('https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true'),
        headers: {
          'Authorization': 'Bearer ${auth.accessToken}',
          'Accept': 'application/json',
        },
      );

      if (channelResponse.statusCode == 200) {
        final channelData = json.decode(channelResponse.body);
        if (channelData['items'] != null && channelData['items'].isNotEmpty) {
          channelName = channelData['items'][0]['snippet']['title'];
          channelId = channelData['items'][0]['id'];
          debugPrint('📺 Found YouTube channel: $channelName');
        }
      }
    } catch (e) {
      debugPrint('⚠️ Could not fetch channel info: $e');
      // Continue anyway - channel info is not critical
    }

    // STEP 7: Send to backend using native-auth endpoint (matching working implementation)
    final success = await _sendNativeAuthToBackend(
      username,
      account,
      auth,
      serverAuthCode,
      channelName,
      channelId
    );

    if (success) {
      _showSuccessSnackBar(context, 'Successfully connected to YouTube!');
      return true;
    } else {
      _showErrorSnackBar(context, 'Failed to connect to YouTube. Please try again.');
      return false;
    }

  } catch (e) {
    debugPrint('❌ Error during YouTube authentication: $e');

    // Handle specific Google Sign-In error codes
    String errorMessage = 'YouTube connection error';
    if (e.toString().contains('sign_in_canceled')) {
      errorMessage = 'Sign-in was cancelled';
    } else if (e.toString().contains('sign_in_failed')) {
      errorMessage = 'Sign-in failed. Please try again.';
    } else if (e.toString().contains('network_error')) {
      errorMessage = 'Network error. Please check your internet connection.';
    } else if (e.toString().contains('sign_in_required')) {
      errorMessage = 'Please sign in to your Google account';
    }

    _showErrorSnackBar(context, errorMessage);
    return false;
  }
}