diff --git a/mobile-v3/android/app/build.gradle b/mobile-v3/android/app/build.gradle
index ddabf76c1d..f1cae6451d 100644
--- a/mobile-v3/android/app/build.gradle
+++ b/mobile-v3/android/app/build.gradle
@@ -39,14 +39,14 @@ if (localPropertiesFile.exists()) {
}
}
-def googleMapApiKey = appProperties.getProperty('google.maps.key')
+def googleMapApiKey = appProperties.getProperty('google.maps.key') ?: secrets.getProperty('MAPS_API_KEY')
if (googleMapApiKey == null) {
- throw new GradleException("Google Maps Key not found. Define google.maps.key in the key.properties file.")
+ throw new GradleException("Google Maps Key not found. Define either google.maps.key in key.properties or MAPS_API_KEY in secrets.properties")
}
-def googleMapApiKeyDev = appProperties.getProperty('google.maps.key.dev')
+def googleMapApiKeyDev = appProperties.getProperty('google.maps.key.dev') ?: secrets.getProperty('MAPS_API_KEY_DEV')
if (googleMapApiKeyDev == null) {
- throw new GradleException("Google Maps Key not found. Define google.maps.key_dev in the key.properties file.")
+ throw new GradleException("Google Maps Dev Key not found. Define either google.maps.key.dev in key.properties or MAPS_API_KEY_DEV in secrets.properties")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
diff --git a/mobile-v3/android/app/src/main/AndroidManifest.xml b/mobile-v3/android/app/src/main/AndroidManifest.xml
index 73f45b0c83..95007fda61 100644
--- a/mobile-v3/android/app/src/main/AndroidManifest.xml
+++ b/mobile-v3/android/app/src/main/AndroidManifest.xml
@@ -10,7 +10,12 @@
+ android:value="${googleMapsKey}" />
+
+
+
{
-// final AuthRepository authRepository;
-// AuthBloc(this.authRepository) : super(AuthInitial()) {
-// on((event, emit) async {
-// if (event is LoginUser) {
-// try {
-// emit(AuthLoading());
-// await authRepository.loginWithEmailAndPassword(
-// event.username, event.password);
-//
-// emit(AuthLoaded(AuthPurpose.LOGIN));
-// } catch (e) {
-// debugPrint(e.toString());
-// emit(
-// AuthLoadingError(
-// e.toString(),
-// ),
-// );
-// }
-// } else if (event is RegisterUser) {
-// try {
-// emit(AuthLoading());
-//
-// await authRepository.registerWithEmailAndPassword(event.model);
-//
-// emit(AuthLoaded(AuthPurpose.REGISTER));
-// } catch (e) {
-// debugPrint(e.toString());
-// emit(
-// AuthLoadingError(
-// e.toString(),
-// ),
-// );
-// }
-// } else if (event is UseAsGuest) {
-// emit(GuestUser());
-// }
-// });
-// }
-// }
class AuthBloc extends Bloc {
final AuthRepository authRepository;
AuthBloc(this.authRepository) : super(AuthInitial()) {
- //debugPrint("AuthBloc initialized");
-
on(_onAppStarted);
-
on(_onLoginUser);
-
on(_onRegisterUser);
+ on(_onLogoutUser);
on((event, emit) => emit(GuestUser()));
}
-
Future _onAppStarted(AppStarted event, Emitter emit) async {
emit(AuthLoading());
try {
@@ -85,15 +41,15 @@ class AuthBloc extends Bloc {
}
}
-
Future _onLoginUser(LoginUser event, Emitter emit) async {
emit(AuthLoading());
try {
-
- final token = await authRepository.loginWithEmailAndPassword(event.username, event.password);
+ final token = await authRepository.loginWithEmailAndPassword(
+ event.username, event.password);
await HiveRepository.saveData(HiveBoxNames.authBox, 'token', token);
// Save token in Hive
- final savedToken = await HiveRepository.getData('token', HiveBoxNames.authBox);
+ final savedToken =
+ await HiveRepository.getData('token', HiveBoxNames.authBox);
//debugPrint("Saved token: $savedToken");
emit(AuthLoaded(AuthPurpose.LOGIN));
@@ -103,8 +59,8 @@ class AuthBloc extends Bloc {
}
}
-
- Future _onRegisterUser(RegisterUser event, Emitter emit) async {
+ Future _onRegisterUser(
+ RegisterUser event, Emitter emit) async {
emit(AuthLoading());
try {
await authRepository.registerWithEmailAndPassword(event.model);
@@ -116,6 +72,18 @@ class AuthBloc extends Bloc {
}
+ Future _onLogoutUser(LogoutUser event, Emitter emit) async {
+ emit(AuthLoading());
+ try {
+ await HiveRepository.deleteData(
+ 'token', HiveBoxNames.authBox); // Remove token from Hive
+ emit(GuestUser()); // Emit guest state after logout
+ } catch (e) {
+ debugPrint("Logout error: $e");
+ emit(AuthLoadingError("Failed to log out. Please try again."));
+ }
+ }
+
String _extractErrorMessage(dynamic e) {
if (e is Exception) {
return e.toString().replaceAll("Exception:", "").trim();
diff --git a/mobile-v3/lib/src/app/auth/bloc/auth_event.dart b/mobile-v3/lib/src/app/auth/bloc/auth_event.dart
index 8bbdcabf1e..16f752305e 100644
--- a/mobile-v3/lib/src/app/auth/bloc/auth_event.dart
+++ b/mobile-v3/lib/src/app/auth/bloc/auth_event.dart
@@ -26,3 +26,7 @@ class RegisterUser extends AuthEvent {
class UseAsGuest extends AuthEvent {
const UseAsGuest();
}
+
+class LogoutUser extends AuthEvent {
+ const LogoutUser();
+}
\ No newline at end of file
diff --git a/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart b/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart
index 6d88eaa80b..889c726397 100644
--- a/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart
+++ b/mobile-v3/lib/src/app/dashboard/pages/dashboard_page.dart
@@ -106,24 +106,24 @@ class _DashboardPageState extends State {
),
SizedBox(width: 8),
GestureDetector(
- onTap: () {
- final authState = context.read().state;
- if (authState is GuestUser) {
+ // onTap: () {
+ // final authState = context.read().state;
+ // if (authState is GuestUser) {
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => GuestProfilePage(),
- ),
- );
- } else {
- // Navigate to the regular profile page
- Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) => ProfilePage(),
- ),
- );
- }
- },
+ // Navigator.of(context).push(
+ // MaterialPageRoute(
+ // builder: (context) => GuestProfilePage(),
+ // ),
+ // );
+ // } else {
+ // // Navigate to the regular profile page
+ // Navigator.of(context).push(
+ // MaterialPageRoute(
+ // builder: (context) => ProfilePage(),
+ // ),
+ // );
+ // }
+ // },
child: BlocBuilder(
builder: (context, authState) {
if (authState is GuestUser) {
diff --git a/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart b/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart
index 8ef0b579c4..88674000d3 100644
--- a/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart
+++ b/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart
@@ -14,19 +14,19 @@ class LessonFinishedWidget extends StatelessWidget {
Text("👋🏼 Great Job !",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700)),
Text(
- "You can invite your friends to learn a thing about Air Pollution",
+ "You can now teach your friends to learn a thing about Air Pollution",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)),
SizedBox(height: 64),
- SmallRoundedButton(
- label: "Share",
- imagePath: "assets/images/shared/share_icon.svg",
- ),
- SizedBox(height: 16),
- SmallRoundedButton(
- label: "Rate the App",
- imagePath: "assets/images/shared/bookmark_icon.svg",
- ),
+ // SmallRoundedButton(
+ // label: "Share",
+ // imagePath: "assets/images/shared/share_icon.svg",
+ // ),
+ // SizedBox(height: 16),
+ // SmallRoundedButton(
+ // label: "Rate the App",
+ // imagePath: "assets/images/shared/bookmark_icon.svg",
+ // ),
],
),
);
diff --git a/mobile-v3/lib/src/app/profile/pages/widgets/settings_widget.dart b/mobile-v3/lib/src/app/profile/pages/widgets/settings_widget.dart
index 1a02748f34..2410a330d3 100644
--- a/mobile-v3/lib/src/app/profile/pages/widgets/settings_widget.dart
+++ b/mobile-v3/lib/src/app/profile/pages/widgets/settings_widget.dart
@@ -1,3 +1,6 @@
+import 'package:airqo/src/app/auth/pages/welcome_screen.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:airqo/src/app/auth/bloc/auth_bloc.dart';
import 'package:flutter/material.dart';
import 'package:airqo/src/app/profile/pages/widgets/settings_tile.dart';
import 'package:flutter_svg/svg.dart';
@@ -9,6 +12,7 @@ class SettingsWidget extends StatefulWidget {
@override
State createState() => _SettingsWidgetState();
}
+
class _SettingsWidgetState extends State {
String _appVersion = '';
bool _locationEnabled = true;
@@ -30,20 +34,16 @@ class _SettingsWidgetState extends State {
void _showLogoutConfirmation() {
showDialog(
context: context,
- builder: (context) => AlertDialog(
+ builder: (dialogContext) => AlertDialog(
title: const Text('Confirm Logout'),
content: const Text('Are you sure you want to log out?'),
actions: [
TextButton(
- onPressed: () => Navigator.pop(context),
+ onPressed: () => Navigator.pop(dialogContext),
child: const Text('Cancel'),
),
ElevatedButton(
- onPressed: () {
- // TODO: Implement actual logout logic
- // e.g., clear user session, revoke tokens
- Navigator.of(context).pushReplacementNamed('/login');
- },
+ onPressed: () => _handleLogout(dialogContext),
child: const Text('Log Out'),
),
],
@@ -51,6 +51,47 @@ class _SettingsWidgetState extends State {
);
}
+ Future _handleLogout(BuildContext dialogContext) async {
+ Navigator.pop(dialogContext); // Close confirmation dialog
+
+ showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (_) => const Center(child: CircularProgressIndicator()),
+ );
+
+ try {
+ context.read().add(LogoutUser());
+
+ await for (final state in context.read().stream) {
+ if (state is GuestUser) {
+ Navigator.pop(context);
+
+ await Navigator.pushAndRemoveUntil(
+ context,
+ MaterialPageRoute(builder: (_) => WelcomeScreen()),
+ (route) => false,
+ );
+ break;
+ } else if (state is AuthLoadingError) {
+ Navigator.pop(context);
+
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(state.message)),
+ );
+ break;
+ }
+ }
+ } catch (e) {
+ Navigator.pop(context);
+
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('An unexpected error occurred')),
+ );
+ }
+}
+
+
void _showDeleteAccountDialog() {
final TextEditingController passwordController = TextEditingController();