mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
519 lines
18 KiB
Dart
519 lines
18 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);
|
|
on<AnalyticsClearAllSpaceTreeSelectionsEvent>(
|
|
_onAnalyticsClearAllSpaceTreeSelectionsEvent,
|
|
);
|
|
}
|
|
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: [],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onAnalyticsClearAllSpaceTreeSelectionsEvent(
|
|
AnalyticsClearAllSpaceTreeSelectionsEvent event,
|
|
Emitter<SpaceTreeState> emit,
|
|
) async {
|
|
emit(
|
|
state.copyWith(
|
|
selectedCommunities: [],
|
|
selectedCommunityAndSpaces: {},
|
|
selectedSpaces: [],
|
|
soldCheck: [],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> close() async {
|
|
_timer.cancel();
|
|
super.close();
|
|
}
|
|
}
|