Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix settings #2546

Merged
merged 4 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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