Files
AmirReza Jamali 35af3e94ea Add liked tenders functionality to HomeRepository and ViewModel
- Integrated LikedTendersService into HomeRepository to fetch the count of user-liked tenders.
- Updated HomeViewModel to load and manage the userLikedTendersCount.
- Adjusted UI components across home pages to display the liked tenders count.
- Refactored date handling in date_utils.dart to improve timestamp parsing and validation.
- Enhanced unit tests for date utilities to cover new edge cases.
2026-05-24 18:23:37 +03:30

237 lines
6.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_response/tender_approvals_response.dart';
import 'package:tm_app/data/services/model/tender_approvals_stats_response/tender_approvals_stats_response.dart';
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
import '../data/repositories/home_repository.dart';
import '../data/services/model/home/home_response/home_response_model.dart';
import '../data/services/model/request_models/get_tenders_request_model/get_tenders_request_model.dart';
import '../data/services/notification_state_service.dart';
class HomeViewModel with ChangeNotifier {
final HomeRepository _homeRepository;
final NotificationStateService _notificationStateService;
HomeViewModel({
required HomeRepository homeRepository,
required NotificationStateService notificationStateService,
}) : _homeRepository = homeRepository,
_notificationStateService = notificationStateService {
_notificationStateService.addListener(_onNotificationStateChanged);
init();
}
void _onNotificationStateChanged() {
notifyListeners();
}
@override
void dispose() {
_notificationStateService.removeListener(_onNotificationStateChanged);
super.dispose();
}
bool _isLoading = false;
String? _errorMessage;
HomeResponseModel? _homeResponse;
TenderApprovalsResponse? data;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
HomeResponseModel? get homeResponse => _homeResponse;
RecommendedTendersResponse? _recommendedTendersResponse;
TenderApprovalsStatsResponse? tenderApprovalsStateResponse;
int userLikedTendersCount = 0;
final List<TenderData> _tenders = [];
final int _pageSize = 10;
int _currentPage = 0;
bool _isLoadingMore = false;
bool _hasMore = true;
bool _isRecommendedMode = false;
List<TenderData> get tenders => _tenders;
bool get isLoadingMore => _isLoadingMore;
bool get hasMore => _hasMore;
bool get isRecommendedMode => _isRecommendedMode;
bool get hasUnreadNotification =>
_notificationStateService.hasUnreadNotification;
void init() async {
_isLoading = true;
_errorMessage = null;
userLikedTendersCount = 0;
_tenders.clear();
_currentPage = 0;
_hasMore = true;
_isRecommendedMode = false;
data = null;
tenderApprovalsStateResponse = null;
notifyListeners();
await getApprovedStates();
await getYourTenders(reset: true);
await loadUserLikedTendersCount();
if (_errorMessage != null) {
_isLoading = false;
notifyListeners();
return;
}
_isLoading = false;
notifyListeners();
}
Future<void> getHomeRecommendTenders({bool reset = false}) async {
if (_isLoadingMore || (!_hasMore && !reset)) {
return;
}
if (reset) {
_currentPage = 0;
_hasMore = true;
_tenders.clear();
}
_isLoadingMore = true;
_errorMessage = null;
_isRecommendedMode = true;
notifyListeners();
final result = await _homeRepository.getHomeRecommendedTenders(
limit: _pageSize,
offset: _currentPage * _pageSize,
);
switch (result) {
case Ok<RecommendedTendersResponse>():
_recommendedTendersResponse = result.value;
if (_recommendedTendersResponse?.data?.tenders.isNotEmpty ?? false) {
_tenders.addAll(
_recommendedTendersResponse!.data!.tenders.whereType<TenderData>(),
);
_currentPage++;
} else {
_hasMore = false;
}
break;
case Error<RecommendedTendersResponse>():
_errorMessage = result.error.toString();
break;
}
_isLoadingMore = false;
notifyListeners();
}
Future<void> getYourTenders({bool reset = false}) async {
if (_isLoadingMore || (!_hasMore && !reset)) {
return;
}
if (reset) {
_currentPage = 0;
_hasMore = true;
_tenders.clear();
}
_isLoadingMore = true;
_isRecommendedMode = false;
notifyListeners();
final requestModel = GetTendersRequestModel(
limit: _pageSize,
offset: _currentPage * _pageSize,
);
final result = await _homeRepository.getYourTenders(
requestModel: requestModel,
);
switch (result) {
case Ok<TenderApprovalsResponse>():
data = result.value;
final tendersList = data?.data?.tenders ?? [];
final metadata = data?.data?.metadata;
if (tendersList.isNotEmpty) {
_tenders.addAll(
tendersList.map((e) => e.tender).whereType<TenderData>(),
);
_currentPage++;
if (metadata != null && (_currentPage >= (metadata.pages ?? 0))) {
_hasMore = false;
}
} else {
_hasMore = false;
_isLoadingMore = false;
getHomeRecommendTenders(reset: true);
}
break;
case Error<TenderApprovalsResponse>():
_errorMessage = result.error.toString();
break;
}
_isLoadingMore = false;
notifyListeners();
}
Future<void> getApprovedStates() async {
final result = await _homeRepository.getTendersApprovalsStats();
switch (result) {
case Ok<TenderApprovalsStatsResponse>():
tenderApprovalsStateResponse = result.value;
break;
case Error<TenderApprovalsStatsResponse>():
_errorMessage = result.error.toString();
break;
}
notifyListeners();
}
Future<void> loadUserLikedTendersCount() async {
final result = await _homeRepository.getUserLikedTendersCount();
switch (result) {
case Ok<int>():
userLikedTendersCount = result.value;
break;
case Error<int>():
userLikedTendersCount = 0;
break;
}
notifyListeners();
}
double get partnershipCount {
return tenderApprovalsStateResponse?.data?.partnershipCount?.toDouble() ??
0.0;
}
double get selfApplyCount {
return tenderApprovalsStateResponse?.data?.selfApplyCount?.toDouble() ??
0.0;
}
double get totalCount {
return partnershipCount + selfApplyCount;
}
double get partnershipPercent {
return totalCount > 0 ? (partnershipCount / totalCount) * 100 : 0;
}
double get selfApplyPercent {
return totalCount > 0 ? (selfApplyCount / totalCount) * 100 : 0;
}
}