diff --git a/lib/pages/space_management_v2/main_module/views/space_management_page.dart b/lib/pages/space_management_v2/main_module/views/space_management_page.dart index 4c3c7452..93e2684f 100644 --- a/lib/pages/space_management_v2/main_module/views/space_management_page.dart +++ b/lib/pages/space_management_v2/main_module/views/space_management_page.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_management_body.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/communities_pagination_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/services/communities_service.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart'; @@ -46,37 +46,20 @@ class SpaceManagementPage extends StatelessWidget { class _FakeCommunitiesService extends CommunitiesService { @override - Future> getCommunity(LoadCommunitiesParam param) async { - return Future.delayed( - const Duration(seconds: 1), - () => [ - const CommunityModel( + Future getCommunity(LoadCommunitiesParam param) { + return Future.value(const CommunitiesPaginationModel( + communities: [ + CommunityModel( uuid: '1', name: 'Community 1', - spaces: [ - SpaceModel( - uuid: '3', - spaceName: 'Space 1', - icon: 'assets/icons/space.png', - children: [ - SpaceModel( - uuid: '4', - spaceName: 'Space 2', - icon: 'assets/icons/space.png', - children: [], - status: SpaceStatus.active, - ), - ], - status: SpaceStatus.active, - ), - ], - ), - const CommunityModel( - uuid: '2', - name: 'Community 1', spaces: [], ), ], - ); + page: 1, + size: 10, + hasNext: false, + totalItems: 2, + totalPages: 1, + )); } } diff --git a/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart b/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart index 83a212ca..e4202398 100644 --- a/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart +++ b/lib/pages/space_management_v2/modules/communities/data/services/remote_communities_service.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/communities_pagination_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/services/communities_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; @@ -15,28 +15,25 @@ class RemoteCommunitiesService implements CommunitiesService { static const _defaultErrorMessage = 'Failed to load communities'; @override - Future> getCommunity(LoadCommunitiesParam param) async { + Future getCommunity(LoadCommunitiesParam param) async { final projectUuid = await ProjectManager.getProjectUUID(); if (projectUuid == null) throw APIException('Project UUID is not set'); try { - final allCommunities = []; - await _httpService.get( + final response = await _httpService.get( path: await _makeUrl(), + queryParameters: { + 'page': param.page, + 'size': param.size, + 'includeSpaces': param.includeSpaces, + if (param.search.isNotEmpty) 'search': param.search, + }, expectedResponseModel: (json) { - final response = json as Map; - final jsonData = response['data'] as List? ?? []; - return jsonData - .map( - (jsonItem) => CommunityModel.fromJson( - jsonItem as Map, - ), - ) - .toList(); + return CommunitiesPaginationModel.fromJson(json as Map); }, ); - return allCommunities; + return response; } on DioException catch (e) { final message = e.response?.data as Map?; final error = message?['error'] as Map?; diff --git a/lib/pages/space_management_v2/modules/communities/domain/models/communities_pagination_model.dart b/lib/pages/space_management_v2/modules/communities/domain/models/communities_pagination_model.dart new file mode 100644 index 00000000..f13ef8ba --- /dev/null +++ b/lib/pages/space_management_v2/modules/communities/domain/models/communities_pagination_model.dart @@ -0,0 +1,69 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; + +class CommunitiesPaginationModel extends Equatable { + const CommunitiesPaginationModel({ + required this.communities, + required this.page, + required this.size, + required this.hasNext, + required this.totalItems, + required this.totalPages, + }); + + final List communities; + final int page; + final int size; + final bool hasNext; + final int totalItems; + final int totalPages; + + const CommunitiesPaginationModel.empty() + : communities = const [], + page = 1, + size = 25, + hasNext = false, + totalItems = 0, + totalPages = 0; + + factory CommunitiesPaginationModel.fromJson(Map json) { + return CommunitiesPaginationModel( + communities: (json['data'] as List? ?? []) + .map((e) => CommunityModel.fromJson(e as Map)) + .toList(), + page: json['page'] as int? ?? 1, + size: json['size'] as int? ?? 25, + hasNext: json['hasNext'] as bool? ?? false, + totalItems: json['totalItems'] as int? ?? 0, + totalPages: json['totalPages'] as int? ?? 0, + ); + } + + CommunitiesPaginationModel copyWith({ + List? communities, + int? page, + int? size, + bool? hasNext, + int? totalItems, + int? totalPages, + }) { + return CommunitiesPaginationModel( + communities: communities ?? this.communities, + page: page ?? this.page, + size: size ?? this.size, + hasNext: hasNext ?? this.hasNext, + totalItems: totalItems ?? this.totalItems, + totalPages: totalPages ?? this.totalPages, + ); + } + + @override + List get props => [ + communities, + page, + size, + hasNext, + totalItems, + totalPages, + ]; +} diff --git a/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart b/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart index 9bdc215c..774c4c31 100644 --- a/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart +++ b/lib/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart @@ -1,3 +1,32 @@ -class LoadCommunitiesParam { - const LoadCommunitiesParam(); +import 'package:equatable/equatable.dart'; + +class LoadCommunitiesParam extends Equatable { + const LoadCommunitiesParam({ + this.page = 1, + this.size = 25, + this.search = '', + this.includeSpaces = true, + }); + + final int page; + final int size; + final String search; + final bool includeSpaces; + + LoadCommunitiesParam copyWith({ + int? page, + int? size, + String? search, + bool? includeSpaces, + }) { + return LoadCommunitiesParam( + page: page ?? this.page, + size: size ?? this.size, + search: search ?? this.search, + includeSpaces: includeSpaces ?? this.includeSpaces, + ); + } + + @override + List get props => [page, size, search, includeSpaces]; } diff --git a/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart b/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart index bccad2ad..564dc4da 100644 --- a/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart +++ b/lib/pages/space_management_v2/modules/communities/domain/services/communities_service.dart @@ -1,6 +1,6 @@ -import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/communities_pagination_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/params/load_communities_param.dart'; abstract class CommunitiesService { - Future> getCommunity(LoadCommunitiesParam param); + Future getCommunity(LoadCommunitiesParam param); } diff --git a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart index 0d85b22f..47dd43f8 100644 --- a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart +++ b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart @@ -14,6 +14,8 @@ class CommunitiesBloc extends Bloc { }) : _communitiesService = communitiesService, super(const CommunitiesState()) { on(_onLoadCommunities); + on(_onLoadMoreCommunities); + on(_onSearchCommunities); } final CommunitiesService _communitiesService; @@ -23,24 +25,113 @@ class CommunitiesBloc extends Bloc { Emitter emit, ) async { try { - emit(const CommunitiesState(status: CommunitiesStatus.loading)); - final communities = await _communitiesService.getCommunity(event.param); + emit(state.copyWith(status: CommunitiesStatus.loading)); + + final paginationResponse = await _communitiesService.getCommunity(event.param); + emit( CommunitiesState( status: CommunitiesStatus.success, - communities: communities, + communities: paginationResponse.communities, + hasNext: paginationResponse.hasNext, + currentPage: paginationResponse.page, + searchQuery: event.param.search, ), ); } on APIException catch (e) { emit( - CommunitiesState( + state.copyWith( status: CommunitiesStatus.failure, errorMessage: e.message, ), ); } catch (e) { + emit( + state.copyWith( + status: CommunitiesStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future _onLoadMoreCommunities( + LoadMoreCommunities event, + Emitter emit, + ) async { + if (!state.hasNext || state.isLoadingMore) return; + + try { + emit(state.copyWith(isLoadingMore: true)); + + final param = LoadCommunitiesParam( + page: state.currentPage + 1, + search: state.searchQuery, + ); + + final paginationResponse = await _communitiesService.getCommunity(param); + + final updatedCommunities = List.from(state.communities) + ..addAll(paginationResponse.communities); + + emit( + state.copyWith( + communities: updatedCommunities, + hasNext: paginationResponse.hasNext, + currentPage: paginationResponse.page, + isLoadingMore: false, + ), + ); + } on APIException catch (e) { + emit( + state.copyWith( + isLoadingMore: false, + errorMessage: e.message, + ), + ); + } catch (e) { + emit( + state.copyWith( + isLoadingMore: false, + errorMessage: e.toString(), + ), + ); + } + } + + Future _onSearchCommunities( + SearchCommunities event, + Emitter emit, + ) async { + try { + emit(state.copyWith(status: CommunitiesStatus.loading)); + + final param = LoadCommunitiesParam( + page: 1, + search: event.searchQuery, + ); + + final paginationResponse = await _communitiesService.getCommunity(param); + emit( CommunitiesState( + status: CommunitiesStatus.success, + communities: paginationResponse.communities, + hasNext: paginationResponse.hasNext, + currentPage: paginationResponse.page, + searchQuery: event.searchQuery, + ), + ); + } on APIException catch (e) { + emit( + state.copyWith( + status: CommunitiesStatus.failure, + errorMessage: e.message, + ), + ); + } catch (e) { + emit( + state.copyWith( status: CommunitiesStatus.failure, errorMessage: e.toString(), ), diff --git a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart index ef375c5a..aa6eda17 100644 --- a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart +++ b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_event.dart @@ -15,3 +15,19 @@ class LoadCommunities extends CommunitiesEvent { @override List get props => [param]; } + +class LoadMoreCommunities extends CommunitiesEvent { + const LoadMoreCommunities(); + + @override + List get props => []; +} + +class SearchCommunities extends CommunitiesEvent { + const SearchCommunities(this.searchQuery); + + final String searchQuery; + + @override + List get props => [searchQuery]; +} diff --git a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart index 94740f0b..c0e57ffd 100644 --- a/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart +++ b/lib/pages/space_management_v2/modules/communities/presentation/bloc/communities_state.dart @@ -7,12 +7,48 @@ final class CommunitiesState extends Equatable { this.status = CommunitiesStatus.initial, this.communities = const [], this.errorMessage, + this.isLoadingMore = false, + this.hasNext = false, + this.currentPage = 1, + this.searchQuery = '', }); final CommunitiesStatus status; final List communities; final String? errorMessage; + final bool isLoadingMore; + final bool hasNext; + final int currentPage; + final String searchQuery; + + CommunitiesState copyWith({ + CommunitiesStatus? status, + List? communities, + String? errorMessage, + bool? isLoadingMore, + bool? hasNext, + int? currentPage, + String? searchQuery, + }) { + return CommunitiesState( + status: status ?? this.status, + communities: communities ?? this.communities, + errorMessage: errorMessage ?? this.errorMessage, + isLoadingMore: isLoadingMore ?? this.isLoadingMore, + hasNext: hasNext ?? this.hasNext, + currentPage: currentPage ?? this.currentPage, + searchQuery: searchQuery ?? this.searchQuery, + ); + } @override - List get props => [status, communities, errorMessage]; + List get props => [ + status, + communities, + errorMessage, + isLoadingMore, + hasNext, + currentPage, + searchQuery, + ]; }