Merged with dev

This commit is contained in:
Abdullah Alassaf
2025-02-20 13:03:38 +03:00
5 changed files with 130 additions and 80 deletions

View File

@ -10,9 +10,12 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/services/auth_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
@ -32,8 +35,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
////////////////////////////// forget password //////////////////////////////////
final TextEditingController forgetEmailController = TextEditingController();
final TextEditingController forgetPasswordController =
TextEditingController();
final TextEditingController forgetPasswordController = TextEditingController();
final TextEditingController forgetOtp = TextEditingController();
final forgetFormKey = GlobalKey<FormState>();
final forgetEmailKey = GlobalKey<FormState>();
@ -50,8 +52,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
return;
}
_remainingTime = 1;
add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
try {
forgetEmailValidate = '';
_remainingTime = (await AuthenticationAPI.sendOtp(
@ -88,8 +89,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
_timer?.cancel();
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
} else {
add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
}
});
}
@ -99,8 +99,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
}
Future<void> changePassword(
ChangePasswordEvent event, Emitter<AuthState> emit) async {
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
emit(LoadingForgetState());
try {
var response = await AuthenticationAPI.verifyOtp(
@ -116,8 +115,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage =
errorData['error']['message'] ?? 'something went wrong';
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
validate = errorMessage;
emit(AuthInitialState());
}
@ -131,9 +129,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
emit(TimerState(
isButtonEnabled: event.isButtonEnabled,
remainingTime: event.remainingTime));
emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
}
///////////////////////////////////// login /////////////////////////////////////
@ -183,15 +179,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
if (token.accessTokenIsNotEmpty) {
FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write(
key: Token.loginAccessTokenKey, value: token.accessToken);
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write(
key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString());
user = UserModel.fromToken(token);
loginEmailController.clear();
loginPasswordController.clear();
debugPrint("token " + token.accessToken);
emit(LoginSuccess());
} else {
emit(const LoginFailure(error: 'Something went wrong'));
@ -342,14 +336,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
static Future<String> getTokenAndValidate() async {
try {
const storage = FlutterSecureStorage();
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
StringsManager.firstLaunch) ??
true;
final firstLaunch =
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
if (firstLaunch) {
storage.deleteAll();
}
await SharedPreferencesHelper.saveBoolToSP(
StringsManager.firstLaunch, false);
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
if (value.isEmpty) {
return 'Token not found';
@ -402,9 +394,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
final String formattedTime = [
if (days > 0) '${days}d', // Append 'd' for days
if (days > 0 || hours > 0)
hours
.toString()
.padLeft(2, '0'), // Show hours if there are days or hours
hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
minutes.toString().padLeft(2, '0'),
seconds.toString().padLeft(2, '0'),
].join(':');
@ -445,6 +435,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
static Future<void> logout(BuildContext context) async {
final storage = FlutterSecureStorage();
ProjectManager.clearProjectUUID();
context.read<SpaceTreeBloc>().add(ClearAllData());
storage.deleteAll();
}
}

View File

@ -2,20 +2,19 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/home_model/home_item_model.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_event.dart';
import 'package:syncrow_web/services/home_api.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> {
// final Graph graph = Graph()..isTree = true;
@ -52,12 +51,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try {
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid);
if (user != null && user!.project != null) {
await ProjectManager.setProjectUUID(user!.project!.uuid);
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
}
add(FetchTermEvent());
add(FetchPolicyEvent());
@ -93,12 +92,10 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
}
}
Future _confirmUserAgreement(
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
try {
emit(LoadingHome());
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
policy = await HomeApi().confirmUserAgreements(uuid);
emit(PolicyAgreement());
} catch (e) {
@ -122,6 +119,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.accessIcon,
active: true,
onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.accessManagementPage);
},
color: null,
@ -131,6 +129,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.spaseManagementIcon,
active: true,
onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.spacesManagementPage);
},
color: ColorsManager.primaryColor,
@ -140,6 +139,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.devicesIcon,
active: true,
onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
BlocProvider.of<RoutineBloc>(context)
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context.go(RoutesConst.deviceManagementPage);

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
@ -6,18 +5,8 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
// String selectedCommunityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
// String selectedSpaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
final TextEditingController textController = TextEditingController();
// String selectedCommunityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
// String selectedSpaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
SpaceTreeBloc() : super(const SpaceTreeState()) {
on<InitialEvent>(_fetchSpaces);
on<OnCommunityExpanded>(_onCommunityExpanded);
@ -25,6 +14,8 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
on<OnCommunitySelected>(_onCommunitySelected);
on<OnSpaceSelected>(_onSpaceSelected);
on<SearchQueryEvent>(_onSearch);
on<ClearAllData>(_clearAllData);
on<ClearCachedData>(_clearCachedData);
}
_fetchSpaces(InitialEvent event, Emitter<SpaceTreeState> emit) async {
@ -147,12 +138,12 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
!updatedSoldChecks.contains(event.spaceId)) {
// First click: Select the space and all its children
updatedSelectedSpaces.add(event.spaceId);
updatedSelectedCommunities.add(event.communityId);
updatedSelectedCommunities.add(event.communityModel.uuid);
if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.addAll(childrenIds);
}
List<String> spaces = _getThePathToChild(event.communityId, event.spaceId);
List<String> spaces = _getThePathToChild(event.communityModel.uuid, event.spaceId);
for (String space in spaces) {
if (!updatedSelectedSpaces.contains(space) && !updatedSoldChecks.contains(space)) {
updatedSoldChecks.add(space);
@ -173,17 +164,28 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
}
updatedSoldChecks.remove(event.spaceId);
List<String> parents = _getThePathToChild(event.communityId, event.spaceId);
if (!_parentSelected(parents, updatedSelectedSpaces)) {
List<String> parents =
_getThePathToChild(event.communityModel.uuid, event.spaceId).toSet().toList();
if (updatedSelectedSpaces.isEmpty) {
updatedSoldChecks.removeWhere(parents.contains);
}
if (!_anySpacesSelectedInCommunity(
event.communityId, updatedSelectedSpaces, updatedSoldChecks)) {
updatedSelectedCommunities.remove(event.communityId);
updatedSelectedCommunities.remove(event.communityModel.uuid);
} else {
// Check if any parent has selected children
for (String space in parents) {
if (!_noChildrenSelected(event.communityModel, space, updatedSelectedSpaces, parents)) {
updatedSoldChecks.remove(space);
}
}
if (!_anySpacesSelectedInCommunity(
event.communityModel, updatedSelectedSpaces, updatedSoldChecks)) {
updatedSelectedCommunities.remove(event.communityModel.uuid);
}
}
}
communityAndSpaces[event.communityId] = updatedSelectedSpaces;
communityAndSpaces[event.communityModel.uuid] = updatedSelectedSpaces;
emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities.toSet().toList(),
@ -196,12 +198,24 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
}
}
_parentSelected(List<String> parents, List<String> selectedSpaces) {
for (String space in parents) {
if (selectedSpaces.contains(space)) {
return true;
_noChildrenSelected(
CommunityModel community, String spaceId, List<String> selectedSpaces, List<String> parents) {
if (selectedSpaces.contains(spaceId)) {
return true;
}
List<SpaceModel> children = _getAllChildSpaces(community.spaces);
for (var child in children) {
if (spaceId == child.uuid) {
List<String> ids = _getAllChildIds(child.children);
for (var id in ids) {
if (selectedSpaces.contains(id)) {
return true;
}
}
}
}
return false;
}
@ -229,6 +243,42 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
}
}
_clearAllData(ClearAllData event, Emitter<SpaceTreeState> emit) async {
try {
emit(state.copyWith(
communitiesList: [],
filteredCommunity: [],
isSearching: false,
soldCheck: [],
selectedSpaces: [],
selectedCommunities: [],
selectedCommunityAndSpaces: {},
searchQuery: '',
expandedSpaces: [],
expandedCommunity: []));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_clearCachedData(ClearCachedData event, Emitter<SpaceTreeState> emit) async {
try {
emit(state.copyWith(
communitiesList: state.communityList,
filteredCommunity: [],
isSearching: false,
soldCheck: [],
selectedSpaces: [],
selectedCommunities: [],
selectedCommunityAndSpaces: {},
searchQuery: '',
expandedSpaces: [],
expandedCommunity: []));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
// Helper function to determine if any space or its children match the search query
bool _containsQuery(SpaceModel space, String query) {
final matchesSpace = space.name.toLowerCase().contains(query);
@ -244,21 +294,26 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
ids.add(child.uuid!);
ids.addAll(_getAllChildIds(child.children));
}
return ids;
return ids.toSet().toList();
}
List<SpaceModel> _getAllChildSpaces(List<SpaceModel> spaces) {
List<SpaceModel> children = [];
for (var child in spaces) {
children.add(child);
children.addAll(_getAllChildSpaces(child.children));
}
return children;
}
bool _anySpacesSelectedInCommunity(
String communityId, List<String> selectedSpaces, List<String> partialCheckedList) {
CommunityModel community, List<String> selectedSpaces, List<String> partialCheckedList) {
bool result = false;
for (var community in state.communityList) {
if (community.uuid == communityId) {
List<String> ids = _getAllChildIds(community.spaces);
for (var id in ids) {
result = selectedSpaces.contains(id) || partialCheckedList.contains(id);
if (result) {
return result;
}
}
List<String> ids = _getAllChildIds(community.spaces);
for (var id in ids) {
result = selectedSpaces.contains(id) || partialCheckedList.contains(id);
if (result) {
return result;
}
}
return result;
@ -305,7 +360,6 @@ class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
@override
Future<void> close() async {
textController.dispose();
super.close();
}
}

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
class SpaceTreeEvent extends Equatable {
@ -49,14 +50,14 @@ class OnSpaceExpanded extends SpaceTreeEvent {
}
class OnSpaceSelected extends SpaceTreeEvent {
final String communityId;
final String spaceId;
final List<SpaceModel> children;
final CommunityModel communityModel;
const OnSpaceSelected(this.communityId, this.spaceId, this.children);
const OnSpaceSelected(this.communityModel, this.spaceId, this.children);
@override
List<Object> get props => [communityId, spaceId, children];
List<Object> get props => [communityModel, spaceId, children];
}
class SearchQueryEvent extends SpaceTreeEvent {
@ -67,3 +68,7 @@ class SearchQueryEvent extends SpaceTreeEvent {
@override
List<Object> get props => [searchQuery];
}
class ClearAllData extends SpaceTreeEvent {}
class ClearCachedData extends SpaceTreeEvent {}

View File

@ -100,8 +100,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
state.expandedSpaces.contains(space.uuid),
onItemSelected: () {
context.read<SpaceTreeBloc>().add(
OnSpaceSelected(community.uuid,
space.uuid ?? '', space.children));
OnSpaceSelected(community, space.uuid ?? '',
space.children));
widget.onSelect();
},
onExpansionChanged: () {
@ -114,7 +114,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
state.soldCheck.contains(space.uuid),
isSoldCheck: state.soldCheck.contains(space.uuid),
children: _buildNestedSpaces(
context, state, space, community.uuid),
context, state, space, community),
);
}).toList(),
),
@ -197,7 +197,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
}
List<Widget> _buildNestedSpaces(
BuildContext context, SpaceTreeState state, SpaceModel space, String communityId) {
BuildContext context, SpaceTreeState state, SpaceModel space, CommunityModel community) {
return space.children.map((child) {
return CustomExpansionTileSpaceTree(
isSelected:
@ -208,13 +208,13 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
onItemSelected: () {
context
.read<SpaceTreeBloc>()
.add(OnSpaceSelected(communityId, child.uuid ?? '', child.children));
.add(OnSpaceSelected(community, child.uuid ?? '', child.children));
widget.onSelect();
},
onExpansionChanged: () {
context.read<SpaceTreeBloc>().add(OnSpaceExpanded(communityId, child.uuid ?? ''));
context.read<SpaceTreeBloc>().add(OnSpaceExpanded(community.uuid, child.uuid ?? ''));
},
children: _buildNestedSpaces(context, state, child, communityId),
children: _buildNestedSpaces(context, state, child, community),
);
}).toList();
}