2026-01-31 15:23:18 +05:30
|
|
|
|
// lib/features/auth/services/auth_service.dart
|
|
|
|
|
|
import 'package:flutter/foundation.dart' show kDebugMode, debugPrint;
|
|
|
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
feat: Phase 1 critical gaps — gamification API, Razorpay checkout, Google OAuth, notifications
- Fix gamification endpoints to use Node.js server (app.eventifyplus.com)
- Replace 6 mock gamification methods with real API calls (dashboard, leaderboard, shop, redeem, submit)
- Add booking models, service, payment service (Razorpay), checkout provider
- Add 3-step CheckoutScreen with Razorpay native modal integration
- Add Google OAuth login (Flutter + Django backend)
- Add full notifications system (Django model + 3 endpoints + Flutter UI)
- Register CheckoutProvider, NotificationProvider in main.dart MultiProvider
- Wire notification bell in HomeScreen app bar
- Add razorpay_flutter ^1.3.7 and google_sign_in ^6.2.2 packages
2026-04-04 15:46:53 +05:30
|
|
|
|
import 'package:google_sign_in/google_sign_in.dart';
|
2026-01-31 15:23:18 +05:30
|
|
|
|
import '../../../core/api/api_client.dart';
|
|
|
|
|
|
import '../../../core/api/api_endpoints.dart';
|
2026-03-20 22:40:50 +05:30
|
|
|
|
import '../../../core/auth/auth_guard.dart';
|
2026-01-31 15:23:18 +05:30
|
|
|
|
import '../../../core/storage/token_storage.dart';
|
feat: Phase 1 critical gaps — gamification API, Razorpay checkout, Google OAuth, notifications
- Fix gamification endpoints to use Node.js server (app.eventifyplus.com)
- Replace 6 mock gamification methods with real API calls (dashboard, leaderboard, shop, redeem, submit)
- Add booking models, service, payment service (Razorpay), checkout provider
- Add 3-step CheckoutScreen with Razorpay native modal integration
- Add Google OAuth login (Flutter + Django backend)
- Add full notifications system (Django model + 3 endpoints + Flutter UI)
- Register CheckoutProvider, NotificationProvider in main.dart MultiProvider
- Wire notification bell in HomeScreen app bar
- Add razorpay_flutter ^1.3.7 and google_sign_in ^6.2.2 packages
2026-04-04 15:46:53 +05:30
|
|
|
|
import '../../../core/analytics/posthog_service.dart';
|
2026-01-31 15:23:18 +05:30
|
|
|
|
import '../models/user_model.dart';
|
|
|
|
|
|
|
|
|
|
|
|
class AuthService {
|
|
|
|
|
|
final ApiClient _api = ApiClient();
|
|
|
|
|
|
|
|
|
|
|
|
/// LOGIN → returns UserModel
|
|
|
|
|
|
Future<UserModel> login(String username, String password) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
final res = await _api.post(
|
|
|
|
|
|
ApiEndpoints.login,
|
|
|
|
|
|
body: {
|
|
|
|
|
|
"username": username,
|
|
|
|
|
|
"password": password,
|
|
|
|
|
|
},
|
|
|
|
|
|
requiresAuth: false,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
final token = res['token'];
|
|
|
|
|
|
if (token == null) {
|
|
|
|
|
|
throw Exception('Token missing from response');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
final serverUsername = (res['username'] is String && (res['username'] as String).isNotEmpty) ? res['username'] as String : null;
|
|
|
|
|
|
final serverEmail = (res['email'] is String && (res['email'] as String).isNotEmpty) ? res['email'] as String : null;
|
|
|
|
|
|
// savedEmail is the canonical identifier for this account
|
|
|
|
|
|
final savedEmail = serverEmail ?? username;
|
|
|
|
|
|
// candidate display name (server username or email fallback)
|
|
|
|
|
|
final displayCandidate = serverUsername ?? savedEmail;
|
|
|
|
|
|
|
2026-03-20 22:40:50 +05:30
|
|
|
|
// clear guest mode on successful login
|
|
|
|
|
|
AuthGuard.setGuest(false);
|
|
|
|
|
|
|
2026-01-31 15:23:18 +05:30
|
|
|
|
// save token (TokenStorage stays responsible for token)
|
|
|
|
|
|
await TokenStorage.saveToken(token.toString(), savedEmail);
|
|
|
|
|
|
|
|
|
|
|
|
// persist per-account fields in SharedPreferences
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
// mark current logged-in account
|
|
|
|
|
|
await prefs.setString('current_email', savedEmail);
|
|
|
|
|
|
// always keep a canonical 'email' pointing to current user's email for old callers
|
|
|
|
|
|
await prefs.setString('email', savedEmail);
|
|
|
|
|
|
// save per-account display name (do not overwrite an explicit per-account display name if already set)
|
|
|
|
|
|
final perKey = 'display_name_$savedEmail';
|
|
|
|
|
|
final existingPer = prefs.getString(perKey);
|
|
|
|
|
|
if (existingPer == null || existingPer.trim().isEmpty) {
|
|
|
|
|
|
await prefs.setString(perKey, displayCandidate);
|
|
|
|
|
|
}
|
|
|
|
|
|
// save server-provided email (if any) separately too
|
|
|
|
|
|
if (serverEmail != null) await prefs.setString('server_email', serverEmail);
|
|
|
|
|
|
|
|
|
|
|
|
// Save phone if provided (optional)
|
|
|
|
|
|
if (res['phone_number'] != null) await prefs.setString('phone_number', res['phone_number'].toString());
|
|
|
|
|
|
|
feat: Phase 1 critical gaps — gamification API, Razorpay checkout, Google OAuth, notifications
- Fix gamification endpoints to use Node.js server (app.eventifyplus.com)
- Replace 6 mock gamification methods with real API calls (dashboard, leaderboard, shop, redeem, submit)
- Add booking models, service, payment service (Razorpay), checkout provider
- Add 3-step CheckoutScreen with Razorpay native modal integration
- Add Google OAuth login (Flutter + Django backend)
- Add full notifications system (Django model + 3 endpoints + Flutter UI)
- Register CheckoutProvider, NotificationProvider in main.dart MultiProvider
- Wire notification bell in HomeScreen app bar
- Add razorpay_flutter ^1.3.7 and google_sign_in ^6.2.2 packages
2026-04-04 15:46:53 +05:30
|
|
|
|
PostHogService.instance.identify(savedEmail, properties: {
|
|
|
|
|
|
'username': displayCandidate,
|
|
|
|
|
|
'login_method': 'email',
|
|
|
|
|
|
});
|
|
|
|
|
|
PostHogService.instance.capture('user_logged_in');
|
|
|
|
|
|
|
2026-01-31 15:23:18 +05:30
|
|
|
|
return UserModel.fromJson(res);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (kDebugMode) debugPrint('AuthService.login error: $e');
|
|
|
|
|
|
rethrow;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// REGISTER → returns UserModel
|
|
|
|
|
|
Future<UserModel> register({
|
|
|
|
|
|
required String email,
|
|
|
|
|
|
required String phoneNumber,
|
|
|
|
|
|
required String password,
|
|
|
|
|
|
}) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
final res = await _api.post(
|
|
|
|
|
|
ApiEndpoints.register,
|
|
|
|
|
|
body: {
|
|
|
|
|
|
"email": email,
|
|
|
|
|
|
"phone_number": phoneNumber,
|
|
|
|
|
|
"password": password,
|
|
|
|
|
|
},
|
|
|
|
|
|
requiresAuth: false,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
final token = res['token'];
|
|
|
|
|
|
if (token == null) {
|
|
|
|
|
|
throw Exception('Token missing from response');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
final serverUsername = (res['username'] is String && (res['username'] as String).isNotEmpty) ? res['username'] as String : null;
|
|
|
|
|
|
final serverEmail = (res['email'] is String && (res['email'] as String).isNotEmpty) ? res['email'] as String : null;
|
|
|
|
|
|
final savedEmail = serverEmail ?? email;
|
|
|
|
|
|
final savedUsername = serverUsername ?? savedEmail;
|
|
|
|
|
|
final savedRole = (res['role'] ?? 'user').toString();
|
|
|
|
|
|
final savedPhone = (res['phone_number'] ?? phoneNumber)?.toString();
|
|
|
|
|
|
|
2026-03-20 22:40:50 +05:30
|
|
|
|
// clear guest mode on successful registration
|
|
|
|
|
|
AuthGuard.setGuest(false);
|
|
|
|
|
|
|
2026-01-31 15:23:18 +05:30
|
|
|
|
// Save token + canonical user id for token storage
|
|
|
|
|
|
await TokenStorage.saveToken(token.toString(), savedEmail);
|
|
|
|
|
|
|
|
|
|
|
|
// Persist per-account keys
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
await prefs.setString('current_email', savedEmail);
|
|
|
|
|
|
await prefs.setString('email', savedEmail);
|
|
|
|
|
|
|
|
|
|
|
|
// Set display_name_<email> if not present yet
|
|
|
|
|
|
final perKey = 'display_name_$savedEmail';
|
|
|
|
|
|
final existing = prefs.getString(perKey);
|
|
|
|
|
|
if (existing == null || existing.trim().isEmpty) {
|
|
|
|
|
|
await prefs.setString(perKey, savedUsername);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Optionally save phone/email in prefs
|
|
|
|
|
|
await prefs.setString('server_email', savedEmail);
|
|
|
|
|
|
if (savedPhone != null) await prefs.setString('phone_number', savedPhone);
|
|
|
|
|
|
|
|
|
|
|
|
// Return a simple UserModel (defensive)
|
|
|
|
|
|
return UserModel(
|
|
|
|
|
|
username: savedUsername,
|
|
|
|
|
|
email: savedEmail,
|
|
|
|
|
|
role: savedRole,
|
|
|
|
|
|
token: token.toString(),
|
|
|
|
|
|
phoneNumber: savedPhone,
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (kDebugMode) debugPrint('AuthService.register error: $e');
|
|
|
|
|
|
rethrow;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat: Phase 1 critical gaps — gamification API, Razorpay checkout, Google OAuth, notifications
- Fix gamification endpoints to use Node.js server (app.eventifyplus.com)
- Replace 6 mock gamification methods with real API calls (dashboard, leaderboard, shop, redeem, submit)
- Add booking models, service, payment service (Razorpay), checkout provider
- Add 3-step CheckoutScreen with Razorpay native modal integration
- Add Google OAuth login (Flutter + Django backend)
- Add full notifications system (Django model + 3 endpoints + Flutter UI)
- Register CheckoutProvider, NotificationProvider in main.dart MultiProvider
- Wire notification bell in HomeScreen app bar
- Add razorpay_flutter ^1.3.7 and google_sign_in ^6.2.2 packages
2026-04-04 15:46:53 +05:30
|
|
|
|
/// GOOGLE OAUTH LOGIN → returns UserModel
|
|
|
|
|
|
Future<UserModel> googleLogin() async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
final googleSignIn = GoogleSignIn(scopes: ['email']);
|
|
|
|
|
|
final account = await googleSignIn.signIn();
|
|
|
|
|
|
if (account == null) throw Exception('Google sign-in cancelled');
|
|
|
|
|
|
|
|
|
|
|
|
final auth = await account.authentication;
|
|
|
|
|
|
final idToken = auth.idToken;
|
|
|
|
|
|
if (idToken == null) throw Exception('Failed to get Google ID token');
|
|
|
|
|
|
|
|
|
|
|
|
final res = await _api.post(
|
|
|
|
|
|
ApiEndpoints.googleLogin,
|
|
|
|
|
|
body: {'id_token': idToken},
|
|
|
|
|
|
requiresAuth: false,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
final token = res['token'];
|
|
|
|
|
|
if (token == null) throw Exception('Token missing from response');
|
|
|
|
|
|
|
|
|
|
|
|
final serverEmail = (res['email'] as String?) ?? account.email;
|
|
|
|
|
|
final displayName = (res['username'] as String?) ?? account.displayName ?? serverEmail;
|
|
|
|
|
|
|
|
|
|
|
|
AuthGuard.setGuest(false);
|
|
|
|
|
|
await TokenStorage.saveToken(token.toString(), serverEmail);
|
|
|
|
|
|
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
await prefs.setString('current_email', serverEmail);
|
|
|
|
|
|
await prefs.setString('email', serverEmail);
|
|
|
|
|
|
final perKey = 'display_name_$serverEmail';
|
|
|
|
|
|
if ((prefs.getString(perKey) ?? '').isEmpty) {
|
|
|
|
|
|
await prefs.setString(perKey, displayName);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (res['phone_number'] != null) await prefs.setString('phone_number', res['phone_number'].toString());
|
|
|
|
|
|
|
|
|
|
|
|
PostHogService.instance.identify(serverEmail, properties: {
|
|
|
|
|
|
'username': displayName,
|
|
|
|
|
|
'login_method': 'google',
|
|
|
|
|
|
});
|
|
|
|
|
|
PostHogService.instance.capture('user_logged_in');
|
|
|
|
|
|
|
|
|
|
|
|
return UserModel.fromJson(res);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (kDebugMode) debugPrint('AuthService.googleLogin error: $e');
|
|
|
|
|
|
rethrow;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-31 15:23:18 +05:30
|
|
|
|
/// Logout – clear auth token and current_email (keep per-account display_name entries so they persist)
|
|
|
|
|
|
Future<void> logout() async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
|
|
|
|
// clear token storage (should delete token + auth headers)
|
|
|
|
|
|
await TokenStorage.clear();
|
|
|
|
|
|
// remove current_email marker so no previous account remains current
|
|
|
|
|
|
await prefs.remove('current_email');
|
|
|
|
|
|
// Also remove canonical 'email' pointing to current user
|
|
|
|
|
|
await prefs.remove('email');
|
|
|
|
|
|
// Do not delete display_name_<email> entries — they are per-account and should remain on device.
|
feat: Phase 1 critical gaps — gamification API, Razorpay checkout, Google OAuth, notifications
- Fix gamification endpoints to use Node.js server (app.eventifyplus.com)
- Replace 6 mock gamification methods with real API calls (dashboard, leaderboard, shop, redeem, submit)
- Add booking models, service, payment service (Razorpay), checkout provider
- Add 3-step CheckoutScreen with Razorpay native modal integration
- Add Google OAuth login (Flutter + Django backend)
- Add full notifications system (Django model + 3 endpoints + Flutter UI)
- Register CheckoutProvider, NotificationProvider in main.dart MultiProvider
- Wire notification bell in HomeScreen app bar
- Add razorpay_flutter ^1.3.7 and google_sign_in ^6.2.2 packages
2026-04-04 15:46:53 +05:30
|
|
|
|
PostHogService.instance.capture('user_logged_out');
|
|
|
|
|
|
PostHogService.instance.reset();
|
2026-01-31 15:23:18 +05:30
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (kDebugMode) debugPrint('AuthService.logout warning: $e');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|