Files
syncrow-web/lib/pages/space_tree/bloc/space_tree_bloc.dart

502 lines
17 KiB
Dart

import 'dart:async';
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';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
import 'package:syncrow_web/pages/space_tree/model/pagination_model.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';
class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
SpaceTreeBloc() : super(const SpaceTreeState()) {
on<InitialEvent>(_fetchSpaces);
on<OnCommunityExpanded>(_onCommunityExpanded);
on<OnSpaceExpanded>(_onSpaceExpanded);
on<OnCommunitySelected>(_onCommunitySelected);
on<OnSpaceSelected>(_onSpaceSelected);
on<SearchQueryEvent>(_onSearch);
on<ClearAllData>(_clearAllData);
on<ClearCachedData>(_clearCachedData);
on<OnCommunityAdded>(_onCommunityAdded);
on<OnCommunityUpdated>(_onCommunityUpdate);
on<PaginationEvent>(_fetchPaginationSpaces);
on<DebouncedSearchEvent>(_onDebouncedSearch);
on<SpaceTreeClearSelectionEvent>(_onSpaceTreeClearSelectionEvent);
}
Timer _timer = Timer(const Duration(microseconds: 0), () {});
void _onCommunityUpdate(
OnCommunityUpdated event,
Emitter<SpaceTreeState> emit,
) async {
emit(SpaceTreeLoadingState());
try {
final updatedCommunity = event.updatedCommunity;
final updatedCommunities = List<CommunityModel>.from(state.communityList);
final index = updatedCommunities
.indexWhere((community) => community.uuid == updatedCommunity.uuid);
if (index != -1) {
updatedCommunities[index] = updatedCommunity;
emit(state.copyWith(communitiesList: updatedCommunities));
} else {
emit(const SpaceTreeErrorState('Community not found in the list.'));
}
} catch (e) {
emit(SpaceTreeErrorState('Error updating community: $e'));
}
}
_fetchSpaces(InitialEvent event, Emitter<SpaceTreeState> emit) async {
emit(SpaceTreeLoadingState());
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
PaginationModel paginationModel = await CommunitySpaceManagementApi()
.fetchCommunitiesAndSpaces(projectId: projectUuid, page: 1);
// List<CommunityModel> updatedCommunities = await Future.wait(
// communities.map((community) async {
// List<SpaceModel> spaces =
// await CommunitySpaceManagementApi().getSpaceHierarchy(community.uuid, projectUuid);
// return CommunityModel(
// uuid: community.uuid,
// createdAt: community.createdAt,
// updatedAt: community.updatedAt,
// name: community.name,
// description: community.description,
// spaces: spaces,
// region: community.region,
// );
// }).toList(),
// );
emit(state.copyWith(
communitiesList: paginationModel.communities,
expandedCommunity: [],
expandedSpaces: [],
paginationModel: paginationModel));
} catch (e) {
emit(SpaceTreeErrorState('Error loading communities and spaces: $e'));
}
}
_fetchPaginationSpaces(PaginationEvent event, Emitter<SpaceTreeState> emit) async {
emit(state.copyWith(paginationIsLoading: true));
PaginationModel paginationModel = event.paginationModel;
List<CommunityModel> communities = List<CommunityModel>.from(event.communities);
try {
if (paginationModel.hasNext && state.searchQuery.isEmpty) {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
paginationModel = await CommunitySpaceManagementApi()
.fetchCommunitiesAndSpaces(
projectId: projectUuid,
page: paginationModel.pageNum,
search: state.searchQuery);
communities.addAll(paginationModel.communities);
}
} catch (e) {
emit(SpaceTreeErrorState('Error loading communities and spaces: $e'));
}
emit(state.copyWith(
communitiesList: communities,
paginationModel: paginationModel,
paginationIsLoading: false));
}
void _onCommunityAdded(
OnCommunityAdded event, Emitter<SpaceTreeState> emit) async {
final updatedCommunities = List<CommunityModel>.from(state.communityList);
updatedCommunities.add(event.newCommunity);
emit(state.copyWith(communitiesList: updatedCommunities));
}
_onCommunityExpanded(
OnCommunityExpanded event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedExpandedCommunityList =
List.from(state.expandedCommunities);
if (updatedExpandedCommunityList.contains(event.communityId)) {
updatedExpandedCommunityList.remove(event.communityId);
} else {
updatedExpandedCommunityList.add(event.communityId);
}
emit(state.copyWith(
expandedCommunity: updatedExpandedCommunityList,
));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onSpaceExpanded(OnSpaceExpanded event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedExpandedSpacesList = List.from(state.expandedSpaces);
if (updatedExpandedSpacesList.contains(event.spaceId)) {
updatedExpandedSpacesList.remove(event.spaceId);
} else {
updatedExpandedSpacesList.add(event.spaceId);
}
emit(state.copyWith(expandedSpaces: updatedExpandedSpacesList));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onCommunitySelected(
OnCommunitySelected event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedSelectedCommunities =
List.from(state.selectedCommunities.toSet().toList());
List<String> updatedSelectedSpaces =
List.from(state.selectedSpaces.toSet().toList());
List<String> updatedSoldChecks = List.from(state.soldCheck.toSet().toList());
Map<String, List<String>> communityAndSpaces =
Map.from(state.selectedCommunityAndSpaces);
List<String> selectedSpacesInCommunity =
communityAndSpaces[event.communityId] ?? [];
List<String> childrenIds = _getAllChildIds(event.children);
if (!updatedSelectedCommunities.contains(event.communityId)) {
// Select the community and all its children
updatedSelectedCommunities.add(event.communityId);
updatedSelectedSpaces.addAll(childrenIds);
selectedSpacesInCommunity.addAll(childrenIds);
} else {
// Unselect the community and all its children
updatedSelectedCommunities.remove(event.communityId);
updatedSelectedSpaces.removeWhere(childrenIds.contains);
updatedSoldChecks.removeWhere(childrenIds.contains);
selectedSpacesInCommunity.removeWhere(childrenIds.contains);
}
communityAndSpaces[event.communityId] = selectedSpacesInCommunity;
emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities,
selectedSpaces: updatedSelectedSpaces,
soldCheck: updatedSoldChecks,
selectedCommunityAndSpaces: communityAndSpaces));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onSpaceSelected(OnSpaceSelected event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedSelectedCommunities =
List.from(state.selectedCommunities.toSet().toList());
List<String> updatedSelectedSpaces =
List.from(state.selectedSpaces.toSet().toList());
List<String> updatedSoldChecks = List.from(state.soldCheck.toSet().toList());
Map<String, List<String>> communityAndSpaces =
Map.from(state.selectedCommunityAndSpaces);
List<String> selectedSpacesInCommunity =
communityAndSpaces[event.communityModel.uuid] ?? [];
List<String> childrenIds = _getAllChildIds(event.children);
bool isChildSelected = false;
for (String id in childrenIds) {
if (updatedSelectedSpaces.contains(id)) {
isChildSelected = true;
}
}
if (!updatedSelectedSpaces.contains(event.spaceId) &&
!updatedSoldChecks.contains(event.spaceId)) {
// First click: Select the space and all its children
updatedSelectedSpaces.add(event.spaceId);
updatedSelectedCommunities.add(event.communityModel.uuid);
selectedSpacesInCommunity.add(event.spaceId);
if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.addAll(childrenIds);
selectedSpacesInCommunity.addAll(childrenIds);
}
List<String> spaces =
_getThePathToChild(event.communityModel.uuid, event.spaceId);
for (String space in spaces) {
if (!updatedSelectedSpaces.contains(space) &&
!updatedSoldChecks.contains(space)) {
updatedSoldChecks.add(space);
}
}
} else if (!updatedSoldChecks.contains(event.spaceId) &&
childrenIds.isNotEmpty &&
isChildSelected) {
// Second click: Unselect space but keep children
selectedSpacesInCommunity.remove(event.spaceId);
updatedSelectedSpaces.remove(event.spaceId);
updatedSoldChecks.add(event.spaceId);
} else {
// Third click: Unselect space and all its children
selectedSpacesInCommunity.remove(event.spaceId);
updatedSelectedSpaces.remove(event.spaceId);
if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.removeWhere(childrenIds.contains);
updatedSoldChecks.removeWhere(childrenIds.contains);
selectedSpacesInCommunity.removeWhere(childrenIds.contains);
}
updatedSoldChecks.remove(event.spaceId);
List<String> parents =
_getThePathToChild(event.communityModel.uuid, event.spaceId)
.toSet()
.toList();
if (updatedSelectedSpaces.isEmpty) {
updatedSoldChecks.removeWhere(parents.contains);
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.communityModel.uuid] = selectedSpacesInCommunity;
emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities.toSet().toList(),
selectedSpaces: updatedSelectedSpaces,
soldCheck: updatedSoldChecks,
selectedCommunityAndSpaces: communityAndSpaces));
emit(state.copyWith(selectedSpaces: updatedSelectedSpaces));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_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;
}
_onSearch(SearchQueryEvent event, Emitter<SpaceTreeState> emit) async {
try {
const duration = Duration(seconds: 1);
if (_timer.isActive) {
_timer.cancel(); // clear timer
}
_timer =
Timer(duration, () async => add(DebouncedSearchEvent(event.searchQuery)));
// List<CommunityModel> communities = List.from(state.communityList);
// List<CommunityModel> filteredCommunity = [];
// Filter communities and expand only those that match the query
// filteredCommunity = communities.where((community) {
// final containsQueryInCommunity =
// community.name.toLowerCase().contains(event.searchQuery.toLowerCase());
// final containsQueryInSpaces =
// community.spaces.any((space) => _containsQuery(space, event.searchQuery.toLowerCase()));
// return containsQueryInCommunity || containsQueryInSpaces;
// }).toList();
// emit(state.copyWith(
// filteredCommunity: filteredCommunity,
// isSearching: event.searchQuery.isNotEmpty,
// searchQuery: event.searchQuery));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onDebouncedSearch(
DebouncedSearchEvent event, Emitter<SpaceTreeState> emit) async {
emit(state.copyWith(
isSearching: true,
));
PaginationModel paginationModel = const PaginationModel.emptyConstructor();
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
paginationModel = await CommunitySpaceManagementApi()
.fetchCommunitiesAndSpaces(
projectId: projectUuid, page: 1, search: event.searchQuery);
} catch (_) {}
emit(state.copyWith(
filteredCommunity: paginationModel.communities,
isSearching: false,
searchQuery: event.searchQuery));
}
_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);
// final matchesChildren =
// space.children.any((child) => _containsQuery(child, query)); // Recursive check for children
// return matchesSpace || matchesChildren;
// }
List<String> _getAllChildIds(List<SpaceModel> spaces) {
List<String> ids = [];
for (var child in spaces) {
ids.add(child.uuid!);
ids.addAll(_getAllChildIds(child.children));
}
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(CommunityModel community,
List<String> selectedSpaces, List<String> partialCheckedList) {
bool result = false;
List<String> ids = _getAllChildIds(community.spaces);
for (var id in ids) {
result = selectedSpaces.contains(id) || partialCheckedList.contains(id);
if (result) {
return result;
}
}
return result;
}
List<String> _getThePathToChild(String communityId, String selectedSpaceId) {
List<String> ids = [];
for (var community in state.communityList) {
if (community.uuid == communityId) {
for (var space in community.spaces) {
List<String> list = [];
list.add(space.uuid!);
ids = _getAllParentsIds(space, selectedSpaceId, List.from(list));
if (ids.isNotEmpty) {
return ids;
}
}
}
}
return ids;
}
List<String> _getAllParentsIds(
SpaceModel child, String spaceId, List<String> listIds) {
List<String> ids = listIds;
ids.add(child.uuid ?? '');
if (child.uuid == spaceId) {
return ids;
}
if (child.children.isNotEmpty) {
for (var space in child.children) {
var result = _getAllParentsIds(space, spaceId, List.from(ids));
if (result.isNotEmpty) {
return result;
}
}
}
ids.removeLast();
return [];
}
void _onSpaceTreeClearSelectionEvent(
SpaceTreeClearSelectionEvent event,
Emitter<SpaceTreeState> emit,
) async {
emit(
state.copyWith(
selectedCommunities: [],
selectedCommunityAndSpaces: {},
selectedSpaces: [],
),
);
}
@override
Future<void> close() async {
_timer.cancel();
super.close();
}
}