Skip to content

Commit

Permalink
Merge pull request #2546 from airqo-platform/fix-settings
Browse files Browse the repository at this point in the history
Fix settings
  • Loading branch information
Mozart299 authored Mar 5, 2025
2 parents 9bacb65 + c04d1d9 commit df17b8d
Show file tree
Hide file tree
Showing 11 changed files with 727 additions and 323 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,51 +47,52 @@ class _LocationSelectionScreenState extends State<LocationSelectionScreen>
UserPreferencesModel? userPreferences;

@override
void initState() {
super.initState();
loggy.info('initState called');
void initState() {
super.initState();
loggy.info('initState called');

_initializeUserData();
_initializeUserData();

googlePlacesBloc = context.read<GooglePlacesBloc>()
..add(ResetGooglePlaces());
googlePlacesBloc = context.read<GooglePlacesBloc>()
..add(ResetGooglePlaces());

loggy.info('Checking dashboard state');
final dashboardBloc = context.read<DashboardBloc>();
final currentState = dashboardBloc.state;
loggy.info('Current dashboard state: ${currentState.runtimeType}');
loggy.info('Checking dashboard state');
final dashboardBloc = context.read<DashboardBloc>();
final currentState = dashboardBloc.state;
loggy.info('Current dashboard state: ${currentState.runtimeType}');

if (currentState is DashboardLoaded) {
loggy.info('Dashboard already loaded, populating measurements');
if (currentState.response.measurements != null) {
loggy.info(
'Found ${currentState.response.measurements!.length} measurements in loaded state');
_populateMeasurements(currentState.response.measurements!);

// IMPORTANT ADDITION: Pre-select existing locations from the current DashboardState
if (currentState.userPreferences != null &&
currentState.userPreferences!.selectedSites.isNotEmpty) {
final existingIds = currentState.userPreferences!.selectedSites
.map((site) => site.id)
.toSet();

if (currentState is DashboardLoaded) {
loggy.info('Dashboard already loaded, populating measurements');
if (currentState.response.measurements != null) {
loggy.info(
'Found ${currentState.response.measurements!.length} measurements in loaded state');
_populateMeasurements(currentState.response.measurements!);

// IMPORTANT ADDITION: Pre-select existing locations from the current DashboardState
if (currentState.userPreferences != null &&
currentState.userPreferences!.selectedSites.isNotEmpty) {
final existingIds = currentState.userPreferences!.selectedSites
.map((site) => site.id)
.toSet();

loggy.info('Pre-selecting ${existingIds.length} existing locations from dashboard state');
loggy.info(
'Pre-selecting ${existingIds.length} existing locations from dashboard state');
setState(() {
selectedLocations = existingIds;
});
}
} else {
loggy.warning('No measurements in loaded state');
setState(() {
selectedLocations = existingIds;
isLoading = false;
errorMessage = "No measurements available in loaded state";
});
}
} else {
loggy.warning('No measurements in loaded state');
setState(() {
isLoading = false;
errorMessage = "No measurements available in loaded state";
});
loggy.info('Dispatching LoadDashboard event');
dashboardBloc.add(LoadDashboard());
}
} else {
loggy.info('Dispatching LoadDashboard event');
dashboardBloc.add(LoadDashboard());
}
}

Future<void> _initializeUserData() async {
try {
Expand Down Expand Up @@ -174,100 +175,99 @@ void initState() {
// File: src/mobile-v3/lib/src/app/dashboard/pages/location_selection/location_selection_screen.dart

// Update the _saveSelectedLocations method in the LocationSelectionScreen
Future<void> _saveSelectedLocations() async {
loggy.info(
'Save button pressed with ${selectedLocations.length} selected locations');
Future<void> _saveSelectedLocations() async {
loggy.info(
'Save button pressed with ${selectedLocations.length} selected locations');

// Debug token
await AuthHelper.debugToken();

// Check auth state from the bloc
final authState = context.read<AuthBloc>().state;
final isLoggedIn = authState is AuthLoaded;

loggy.info('Current auth state: ${authState.runtimeType}');
loggy.info('Is user logged in? $isLoggedIn');

if (!isLoggedIn) {
loggy.warning('❌ User not logged in, cannot save');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please log in to save your locations')),
);
return;
}

// Debug token
await AuthHelper.debugToken();
// Use enhanced token checker
final isExpired = await TokenDebugger.checkTokenExpiration();

if (isExpired) {
loggy.warning('❌ Token is expired, cannot save');

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Your session has expired. Please log in again.'),
duration: const Duration(seconds: 8),
action: SnackBarAction(
label: 'Log In',
onPressed: () {
// Navigate directly to login screen
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const LoginPage(),
),
(route) => false,
);
},
),
),
);
return;
}

// Check auth state from the bloc
final authState = context.read<AuthBloc>().state;
final isLoggedIn = authState is AuthLoaded;
setState(() {
isSaving = true;
});

loggy.info('Current auth state: ${authState.runtimeType}');
loggy.info('Is user logged in? $isLoggedIn');
try {
// IMPORTANT CHANGE: Instead of creating a new preference, we dispatch
// the UpdateSelectedLocations event to the DashboardBloc, which will
// merge these with existing locations

if (!isLoggedIn) {
loggy.warning('❌ User not logged in, cannot save');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please log in to save your locations')),
);
return;
}
final dashboardBloc = context.read<DashboardBloc>();

// Use enhanced token checker
final isExpired = await TokenDebugger.checkTokenExpiration();

if (isExpired) {
loggy.warning('❌ Token is expired, cannot save');

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Your session has expired. Please log in again.'),
duration: const Duration(seconds: 8),
action: SnackBarAction(
label: 'Log In',
onPressed: () {
// Navigate directly to login screen
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const LoginPage(),
),
(route) => false,
);
},
),
),
);
return;
}

setState(() {
isSaving = true;
});

try {
// IMPORTANT CHANGE: Instead of creating a new preference, we dispatch
// the UpdateSelectedLocations event to the DashboardBloc, which will
// merge these with existing locations

final dashboardBloc = context.read<DashboardBloc>();

// Convert the Set to a List
final locationIdsList = selectedLocations.toList();

loggy.info('Dispatching UpdateSelectedLocations with ${locationIdsList.length} locations');

// Dispatch the event
dashboardBloc.add(UpdateSelectedLocations(locationIdsList));

// Show success message
loggy.info('✅ Successfully dispatched update event');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Locations saved successfully')),
);

// Return to previous screen with the selected locations
Navigator.pop(context, locationIdsList);
} catch (e) {
loggy.error('❌ Error saving locations: $e');
loggy.error('Stack trace: ${StackTrace.current}');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'An error occurred while saving locations: ${e.toString()}')),
);
} finally {
if (mounted) {
setState(() {
isSaving = false;
});
}
}
}
// Convert the Set to a List
final locationIdsList = selectedLocations.toList();

loggy.info(
'Dispatching UpdateSelectedLocations with ${locationIdsList.length} locations');

// Dispatch the event
dashboardBloc.add(UpdateSelectedLocations(locationIdsList));

// Show success message
loggy.info('✅ Successfully dispatched update event');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Locations saved successfully')),
);

// Return to previous screen with the selected locations
Navigator.pop(context, locationIdsList);
} catch (e) {
loggy.error('❌ Error saving locations: $e');
loggy.error('Stack trace: ${StackTrace.current}');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'An error occurred while saving locations: ${e.toString()}')),
);
} finally {
if (mounted) {
setState(() {
isSaving = false;
});
}
}
}

Future<void> _loadUserPreferences(String userId) async {
try {
Expand Down
52 changes: 37 additions & 15 deletions src/mobile-v3/lib/src/app/dashboard/widgets/dashboard_app_bar.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:airqo/src/app/auth/pages/login_page.dart';
import 'package:airqo/src/app/profile/pages/profile_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
Expand Down Expand Up @@ -35,7 +37,6 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {

Widget _buildThemeToggle(BuildContext context) {
final themeBloc = context.read<ThemeBloc>();

return GestureDetector(
onTap: () => themeBloc.add(ToggleTheme(true)),
child: CircleAvatar(
Expand All @@ -61,14 +62,24 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
}

Widget _buildGuestAvatar(BuildContext context) {
return CircleAvatar(
backgroundColor: Theme.of(context).highlightColor,
radius: 24,
child: Center(
child: SvgPicture.asset(
"assets/icons/user_icon.svg",
height: 22,
width: 22,
return GestureDetector(
onTap: () {
// Navigate to login/signup screen for guest users
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LoginPage(),
),
);
},
child: CircleAvatar(
backgroundColor: Theme.of(context).highlightColor,
radius: 24,
child: Center(
child: SvgPicture.asset(
"assets/icons/user_icon.svg",
height: 22,
width: 22,
),
),
),
);
Expand All @@ -78,12 +89,23 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
return BlocBuilder<UserBloc, UserState>(
builder: (context, userState) {
if (userState is UserLoaded) {
String firstName = userState.model.users[0].firstName[0].toUpperCase();
String firstName =
userState.model.users[0].firstName[0].toUpperCase();
String lastName = userState.model.users[0].lastName[0].toUpperCase();
return CircleAvatar(
radius: 24,
backgroundColor: Theme.of(context).highlightColor,
child: Center(child: Text("$firstName$lastName")),
return GestureDetector(
onTap: () {
// Navigate to profile page
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProfilePage(),
),
);
},
child: CircleAvatar(
radius: 24,
backgroundColor: Theme.of(context).highlightColor,
child: Center(child: Text("$firstName$lastName")),
),
);
} else if (userState is UserLoadingError) {
return Container(); // Handle error state (optional)
Expand All @@ -97,4 +119,4 @@ class DashboardAppBar extends StatelessWidget implements PreferredSizeWidget {
},
);
}
}
}
29 changes: 13 additions & 16 deletions src/mobile-v3/lib/src/app/dashboard/widgets/dashboard_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,19 @@ class DashboardHeader extends StatelessWidget {
Widget build(BuildContext context) {
return PagePadding(
padding: 16,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16),
_buildGreeting(context),
Text(
"Today's Air Quality • ${DateFormat.MMMMd().format(DateTime.now())}",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.headlineMedium?.color,
),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(height: 16),
_buildGreeting(context),
Text(
"Today's Air Quality • ${DateFormat.MMMMd().format(DateTime.now())}",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.headlineMedium?.color,
),
SizedBox(height: 16)
]
),
),
SizedBox(height: 16)
]),
);
}

Expand Down Expand Up @@ -97,4 +94,4 @@ class DashboardHeader extends StatelessWidget {
},
);
}
}
}
Loading

0 comments on commit df17b8d

Please sign in to comment.