diff --git a/lib/data/repositories/tenders_repository.dart b/lib/data/repositories/tenders_repository.dart index b66ed4d..6d6f5bd 100644 --- a/lib/data/repositories/tenders_repository.dart +++ b/lib/data/repositories/tenders_repository.dart @@ -8,8 +8,11 @@ class TendersRepository { : _tendersService = tendersService; final TendersService _tendersService; - Future> getTenders() async { - return _tendersService.getTenders(); + Future> getTenders({ + required int limit, + required int offset, + }) async { + return _tendersService.getTenders(limit: limit, offset: offset); } Future> getTenderFeedback({ diff --git a/lib/data/services/tenders_service.dart b/lib/data/services/tenders_service.dart index 22536df..e7110c6 100644 --- a/lib/data/services/tenders_service.dart +++ b/lib/data/services/tenders_service.dart @@ -11,9 +11,12 @@ class TendersService { : _networkManager = networkManager; final NetworkManager _networkManager; - Future> getTenders() async { + Future> getTenders({ + required int limit, + required int offset, + }) async { final result = await _networkManager.makeRequest( - '/api/v1/tenders', + '/api/v1/tenders?limit=$limit&offset=$offset', method: 'GET', (json) => TendersResponse.fromJson(json), ); diff --git a/lib/view_models/tenders_view_model.dart b/lib/view_models/tenders_view_model.dart index 91d22ba..e245562 100644 --- a/lib/view_models/tenders_view_model.dart +++ b/lib/view_models/tenders_view_model.dart @@ -19,11 +19,13 @@ class TendersViewModel with ChangeNotifier { } bool _isLoading = false; + bool _isLoadingMore = false; String? _errorMessage; TendersResponse? _tendersResponse; FeedbackData? _feedbackData; bool get isLoading => _isLoading; + bool get isLoadingMore => _isLoadingMore; String? get errorMessage => _errorMessage; TendersResponse? get tendersResponse => _tendersResponse; FeedbackData? get feedbackData => _feedbackData; @@ -31,6 +33,11 @@ class TendersViewModel with ChangeNotifier { List feedbacks = []; int currentPage = 1; + int _currentOffset = 0; + static const int _pageSize = 10; + bool _hasMoreData = true; + + bool get hasMoreData => _hasMoreData; /// Get feedback for a specific tender FeedbackData? getFeedbackForTender(String tenderId) { @@ -49,13 +56,24 @@ class TendersViewModel with ChangeNotifier { Future getTenders() async { _isLoading = true; _errorMessage = null; + _currentOffset = 0; + _hasMoreData = true; notifyListeners(); - final result = await _tendersRepository.getTenders(); + final result = await _tendersRepository.getTenders( + limit: _pageSize, + offset: _currentOffset, + ); switch (result) { case Ok(): _tendersResponse = result.value; - getTenderFeedback(_tendersResponse!.data!.tenders!.first.id!); + // Check if we have more data + if (_tendersResponse?.data?.tenders != null) { + _hasMoreData = _tendersResponse!.data!.tenders!.length >= _pageSize; + } + if (_tendersResponse?.data?.tenders?.isNotEmpty == true) { + getTenderFeedback(_tendersResponse!.data!.tenders!.first.id!); + } break; case Error(): _errorMessage = result.error.toString(); @@ -67,6 +85,54 @@ class TendersViewModel with ChangeNotifier { notifyListeners(); } + Future loadMoreTenders() async { + if (_isLoadingMore || !_hasMoreData) return; + + _isLoadingMore = true; + _errorMessage = null; + notifyListeners(); + + _currentOffset += _pageSize; + final result = await _tendersRepository.getTenders( + limit: _pageSize, + offset: _currentOffset, + ); + + switch (result) { + case Ok(): + if (result.value.data?.tenders != null && + result.value.data!.tenders!.isNotEmpty) { + // Create a new modifiable list by combining existing and new tenders + if (_tendersResponse?.data?.tenders != null) { + final existingTenders = _tendersResponse!.data!.tenders!; + final newTenders = result.value.data!.tenders!; + final combinedTenders = [...existingTenders, ...newTenders]; + + // Create a new TendersData with the combined list + _tendersResponse = _tendersResponse!.copyWith( + data: _tendersResponse!.data!.copyWith(tenders: combinedTenders), + ); + } else { + _tendersResponse = result.value; + } + // Check if we have more data + _hasMoreData = result.value.data!.tenders!.length >= _pageSize; + } else { + _hasMoreData = false; + } + break; + case Error(): + _errorMessage = result.error.toString(); + _currentOffset -= _pageSize; // Revert offset on error + break; + } + + _isLoadingMore = false; + notifyListeners(); + _errorMessage = null; + notifyListeners(); + } + Future getTenderFeedback(String tenderId) async { // _isLoading = true; _errorMessage = null; diff --git a/lib/views/tenders/widgets/main_tenders_slider.dart b/lib/views/tenders/widgets/main_tenders_slider.dart index e48270c..5fe4b0b 100644 --- a/lib/views/tenders/widgets/main_tenders_slider.dart +++ b/lib/views/tenders/widgets/main_tenders_slider.dart @@ -94,11 +94,19 @@ class _MainTendersSliderState extends State { setState(() { currentPage = index + 1; }); + // Fetch feedback for the new tender if not already loaded final viewModel = context.read(); if (!viewModel.hasFeedbackForTender(widget.tenders[index].id!)) { viewModel.getTenderFeedback(widget.tenders[index].id!); } + + // Check if we need to load more tenders (when user reaches near the end) + if (index >= widget.tenders.length - 1 && + viewModel.hasMoreData && + !viewModel.isLoadingMore) { + viewModel.loadMoreTenders(); + } }, itemBuilder: (context, index) { final tender = widget.tenders[index]; @@ -142,13 +150,31 @@ class _MainTendersSliderState extends State { } Widget _pageNumberText() { - return Text( - '$currentPage/${widget.tenders.length}', - style: TextStyle( - fontSize: 14.0.sp(), - fontWeight: FontWeight.w500, - color: AppColors.grey, - ), + final viewModel = context.watch(); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '$currentPage/${widget.tenders.length}', + style: TextStyle( + fontSize: 14.0.sp(), + fontWeight: FontWeight.w500, + color: AppColors.grey, + ), + ), + if (viewModel.isLoadingMore) + Padding( + padding: EdgeInsets.only(top: 4.0.h()), + child: SizedBox( + width: 16.0.w(), + height: 16.0.w(), + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: AppColors.jellyBean, + ), + ), + ), + ], ); }