SP-1737-FE-The-user-appears-as-Null-and-the-project-uuid-is-null-when-we-login-in-after-a-credentials-error (#259)

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1723](https://syncrow.atlassian.net/browse/SP-1723)

## Description

Loads user data on the initial state of `HomePage` instead of loading it
in the `MaterialApp`, because in the previous solution that caused
temporal coupling.

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1723]:
https://syncrow.atlassian.net/browse/SP-1723?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This commit is contained in:
Faris Armoush
2025-06-17 14:33:06 +03:00
committed by GitHub
6 changed files with 57 additions and 33 deletions

View File

@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options_prod.dart'; import 'package:syncrow_web/firebase_options_prod.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
@ -21,8 +20,10 @@ import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async { Future<void> main() async {
try { try {
const environment = const environment = String.fromEnvironment(
String.fromEnvironment('FLAVOR', defaultValue: 'production'); 'FLAVOR',
defaultValue: 'production',
);
await dotenv.load(fileName: '.env.$environment'); await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp( await Firebase.initializeApp(
@ -40,7 +41,7 @@ class MyApp extends StatelessWidget {
initialLocation: RoutesConst.auth, initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(), routes: AppRoutes.getRoutes(),
redirect: (context, state) async { redirect: (context, state) async {
String checkToken = await AuthBloc.getTokenAndValidate(); final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success'; final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth; final goingToLogin = state.uri.toString() == RoutesConst.auth;
@ -58,8 +59,7 @@ class MyApp extends StatelessWidget {
BlocProvider<CreateRoutineBloc>( BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(), create: (context) => CreateRoutineBloc(),
), ),
BlocProvider( BlocProvider(create: (context) => HomeBloc()),
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>( BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
), ),

View File

@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options_dev.dart'; import 'package:syncrow_web/firebase_options_dev.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
@ -21,7 +20,10 @@ import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async { Future<void> main() async {
try { try {
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development'); const environment = String.fromEnvironment(
'FLAVOR',
defaultValue: 'development',
);
await dotenv.load(fileName: '.env.$environment'); await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp( await Firebase.initializeApp(
@ -39,7 +41,7 @@ class MyApp extends StatelessWidget {
initialLocation: RoutesConst.auth, initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(), routes: AppRoutes.getRoutes(),
redirect: (context, state) async { redirect: (context, state) async {
String checkToken = await AuthBloc.getTokenAndValidate(); final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success'; final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth; final goingToLogin = state.uri.toString() == RoutesConst.auth;
@ -57,7 +59,7 @@ class MyApp extends StatelessWidget {
BlocProvider<CreateRoutineBloc>( BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(), create: (context) => CreateRoutineBloc(),
), ),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), BlocProvider(create: (context) => HomeBloc()),
BlocProvider<VisitorPasswordBloc>( BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
), ),

View File

@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options_prod.dart'; import 'package:syncrow_web/firebase_options_prod.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
@ -39,7 +38,7 @@ class MyApp extends StatelessWidget {
initialLocation: RoutesConst.auth, initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(), routes: AppRoutes.getRoutes(),
redirect: (context, state) async { redirect: (context, state) async {
String checkToken = await AuthBloc.getTokenAndValidate(); final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success'; final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth; final goingToLogin = state.uri.toString() == RoutesConst.auth;
@ -57,7 +56,7 @@ class MyApp extends StatelessWidget {
BlocProvider<CreateRoutineBloc>( BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(), create: (context) => CreateRoutineBloc(),
), ),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())), BlocProvider(create: (context) => HomeBloc()),
BlocProvider<VisitorPasswordBloc>( BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
), ),

View File

@ -13,29 +13,32 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/services/home_api.dart'; import 'package:syncrow_web/services/home_api.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> { class HomeBloc extends Bloc<HomeEvent, HomeState> {
UserModel? user; UserModel? user;
String terms = ''; String terms = '';
String policy = ''; String policy = '';
HomeBloc() : super((HomeInitial())) { HomeBloc() : super(HomeInitial()) {
on<FetchUserInfo>(_fetchUserInfo); on<FetchUserInfo>(_fetchUserInfo);
on<FetchTermEvent>(_fetchTerms); on<FetchTermEvent>(_fetchTerms);
on<FetchPolicyEvent>(_fetchPolicy); on<FetchPolicyEvent>(_fetchPolicy);
on<ConfirmUserAgreementEvent>(_confirmUserAgreement); on<ConfirmUserAgreementEvent>(_confirmUserAgreement);
} }
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async { Future<void> _fetchUserInfo(
FetchUserInfo event,
Emitter<HomeState> emit,
) async {
try { try {
var uuid = final uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey); await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid); if (uuid != null) {
user = await HomeApi().fetchUserInfo(uuid);
}
if (user != null && user!.project != null) { if (user != null && user?.project != null) {
await ProjectManager.setProjectUUID(user!.project!.uuid); await ProjectManager.setProjectUUID(user!.project!.uuid);
} }
add(FetchTermEvent()); add(FetchTermEvent());
add(FetchPolicyEvent()); add(FetchPolicyEvent());
@ -46,7 +49,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
} }
} }
Future _fetchTerms(FetchTermEvent event, Emitter<HomeState> emit) async { Future<void> _fetchTerms(FetchTermEvent event, Emitter<HomeState> emit) async {
try { try {
emit(LoadingHome()); emit(LoadingHome());
terms = await HomeApi().fetchTerms(); terms = await HomeApi().fetchTerms();
@ -56,22 +59,22 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
} }
} }
Future _fetchPolicy(FetchPolicyEvent event, Emitter<HomeState> emit) async { Future<void> _fetchPolicy(FetchPolicyEvent event, Emitter<HomeState> emit) async {
try { try {
emit(LoadingHome()); emit(LoadingHome());
policy = await HomeApi().fetchPolicy(); policy = await HomeApi().fetchPolicy();
emit(HomeInitial()); emit(HomeInitial());
} catch (e) { } catch (e) {
debugPrint("Error fetching policy: $e"); debugPrint('Error fetching policy: $e');
return; return;
} }
} }
Future _confirmUserAgreement( Future<void> _confirmUserAgreement(
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async { ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
try { try {
emit(LoadingHome()); emit(LoadingHome());
var uuid = final uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey); await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
policy = await HomeApi().confirmUserAgreements(uuid); policy = await HomeApi().confirmUserAgreements(uuid);
emit(PolicyAgreement()); emit(PolicyAgreement());
@ -80,7 +83,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
} }
} }
List<HomeItemModel> homeItems = [ final List<HomeItemModel> homeItems = [
HomeItemModel( HomeItemModel(
title: 'Access Management', title: 'Access Management',
icon: Assets.accessIcon, icon: Assets.accessIcon,

View File

@ -1,11 +1,25 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/view/home_page_mobile.dart'; import 'package:syncrow_web/pages/home/view/home_page_mobile.dart';
import 'package:syncrow_web/pages/home/view/home_page_web.dart'; import 'package:syncrow_web/pages/home/view/home_page_web.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class HomePage extends StatelessWidget with HelperResponsiveLayout { class HomePage extends StatefulWidget {
const HomePage({super.key}); const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with HelperResponsiveLayout{
@override
void initState() {
context.read<HomeBloc>().add(const FetchUserInfo());
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isSmallScreen = isSmallScreenSize(context); final isSmallScreen = isSmallScreenSize(context);

View File

@ -3,14 +3,20 @@ import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
class HomeApi { class HomeApi {
Future fetchUserInfo(userId) async { Future<UserModel?> fetchUserInfo(String userId) async {
final response = await HTTPService().get( try {
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!), final response = await HTTPService().get(
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return UserModel.fromJson(json); final user = UserModel.fromJson(json as Map<String, dynamic>);
}); return user;
return response; },
);
return response;
} catch (e) {
return null;
}
} }
Future fetchTerms() async { Future fetchTerms() async {