Commit 0bf528a4 authored by DatHV's avatar DatHV
Browse files

refactor logic

parent 89983084
...@@ -41,31 +41,33 @@ class HomeTabViewModel extends RestfulApiViewModel { ...@@ -41,31 +41,33 @@ class HomeTabViewModel extends RestfulApiViewModel {
} }
Future<void> getSectionLayoutHome() async { Future<void> getSectionLayoutHome() async {
showLoading(); await callApi<List<MainSectionConfigModel>>(
try { request: () => client.getSectionLayoutHome(),
final response = await client.getSectionLayoutHome(); onSuccess: (data, _) {
sectionLayouts.assignAll(response.data ?? []); sectionLayouts.assignAll(data);
hideLoading(); },
} catch (error) { onFailure: (_, _, _) async {
sectionLayouts.assignAll(await _loadSectionLayoutHomeFromCache()); sectionLayouts.assignAll(await _loadSectionLayoutHomeFromCache());
hideLoading(); },
} finally { onComplete: () async {
if (sectionLayouts.isEmpty) { if (sectionLayouts.isEmpty) {
sectionLayouts.assignAll(await _loadSectionLayoutHomeFromCache()); sectionLayouts.assignAll(await _loadSectionLayoutHomeFromCache());
} }
for (final section in sectionLayouts) { for (final section in sectionLayouts) {
await _processSection(section); await _processSection(section);
} }
} },
);
} }
Future<void> loadDataPiPiHome() async { Future<void> loadDataPiPiHome() async {
try { await callApi<HoverDataModel>(
final result = await client.getDataPiPiHome(); request: () => client.getDataPiPiHome(),
hoverData.value = result.data; onSuccess: (data, _) {
} catch (error) { hoverData.value = data;
print("Error fetching loadDataPiPiHome: $error"); },
} withLoading: false,
);
} }
Future<List<MainSectionConfigModel>> _loadSectionLayoutHomeFromCache() async { Future<List<MainSectionConfigModel>> _loadSectionLayoutHomeFromCache() async {
......
...@@ -26,7 +26,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel { ...@@ -26,7 +26,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel {
onShowAlertError?.call(Constants.commonError, false); onShowAlertError?.call(Constants.commonError, false);
} }
}, },
onFailure: (msg, _, __) async { onFailure: (msg, _, _) async {
onShowAlertError?.call(msg, false); onShowAlertError?.call(msg, false);
}, },
); );
...@@ -38,7 +38,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel { ...@@ -38,7 +38,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel {
onSuccess: (data, _) { onSuccess: (data, _) {
inviteFriendDetail.value = data; inviteFriendDetail.value = data;
}, },
onFailure: (msg, _, __) async { onFailure: (msg, _, _) async {
onShowAlertError?.call(msg, true); onShowAlertError?.call(msg, true);
}, },
); );
...@@ -50,7 +50,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel { ...@@ -50,7 +50,7 @@ class InviteFriendCampaignViewModel extends RestfulApiViewModel {
onSuccess: (data, _) { onSuccess: (data, _) {
campaignDetail.value = data; campaignDetail.value = data;
}, },
onFailure: (msg, _, __) async { onFailure: (msg, _, _) async {
// Silent failure for this optional call // Silent failure for this optional call
}, },
showAppNavigatorDialog: false, showAppNavigatorDialog: false,
......
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import 'location_address_screen.dart'; import 'location_address_screen.dart';
...@@ -26,31 +24,37 @@ class LocationAddressViewModel extends RestfulApiViewModel { ...@@ -26,31 +24,37 @@ class LocationAddressViewModel extends RestfulApiViewModel {
void onInit() { void onInit() {
super.onInit(); super.onInit();
if (type == LocationAddressType.province) { if (type == LocationAddressType.province) {
client.locationProvinceGetList().then((value) { callApi<ProvinceAddressResponse>(
final data = value.data?.items ?? []; request: () => client.locationProvinceGetList(),
_loadFromProvince(data); onSuccess: (data, _) {
}); _loadFromProvince(data.items ?? []);
},
withLoading: false,
);
} else { } else {
client.locationDistrictGetList(provinceCode).then((value) { callApi<DistrictAddressResponse>(
final data = value.data?.items ?? []; request: () => client.locationDistrictGetList(provinceCode),
_loadFromDistrict(data); onSuccess: (data, _) {
}); _loadFromDistrict(data.items ?? []);
},
withLoading: false,
);
} }
} }
void _loadFromProvince(List<ProvinceAddressModel> provinces) { void _loadFromProvince(List<ProvinceAddressModel> provinces) {
_allItems = provinces.map((e) => AddressBaseModel(code: e.cityCode, name: e.cityName)).toList(); _allItems = provinces.map((e) => AddressBaseModel(code: e.cityCode, name: e.cityName)).toList();
displayItems.value = _allItems; displayItems.assignAll(_allItems);
} }
void _loadFromDistrict(List<DistrictAddressModel> districts) { void _loadFromDistrict(List<DistrictAddressModel> districts) {
_allItems = districts.map((e) => AddressBaseModel(code: e.districtCode, name: e.districtName)).toList(); _allItems = districts.map((e) => AddressBaseModel(code: e.districtCode, name: e.districtName)).toList();
displayItems.value = _allItems; displayItems.assignAll(_allItems);
} }
void search(String query) { void search(String query) {
if (query.isEmpty) { if (query.isEmpty) {
displayItems.value = _allItems; displayItems.assignAll(_allItems);
return; return;
} }
final lowerQuery = _removeDiacritics(query).toLowerCase(); final lowerQuery = _removeDiacritics(query).toLowerCase();
...@@ -59,11 +63,10 @@ class LocationAddressViewModel extends RestfulApiViewModel { ...@@ -59,11 +63,10 @@ class LocationAddressViewModel extends RestfulApiViewModel {
final normalized = _removeDiacritics(name).toLowerCase(); final normalized = _removeDiacritics(name).toLowerCase();
return normalized.contains(lowerQuery); return normalized.contains(lowerQuery);
}).toList(); }).toList();
displayItems.value = filteredItems; displayItems.assignAll(filteredItems);
} }
void select(AddressBaseModel item) { void select(AddressBaseModel item) {
print(" Selected code: ${item.code}");
selectedCode.value = item.code ?? ''; selectedCode.value = item.code ?? '';
displayItems.refresh(); displayItems.refresh();
Get.back(result: item); Get.back(result: item);
......
...@@ -8,15 +8,16 @@ import '../../networking/restful_api_viewmodel.dart'; ...@@ -8,15 +8,16 @@ import '../../networking/restful_api_viewmodel.dart';
import '../../permission/biometric_manager.dart'; import '../../permission/biometric_manager.dart';
import '../../preference/data_preference.dart'; import '../../preference/data_preference.dart';
import '../../services/login_service.dart'; import '../../services/login_service.dart';
import '../otp/model/create_otp_response_model.dart';
enum LoginState { idle, typing, error } enum LoginState { idle, typing, error }
class LoginViewModel extends RestfulApiViewModel { class LoginViewModel extends RestfulApiViewModel {
var loginState = LoginState.idle.obs; final Rx<LoginState> loginState = LoginState.idle.obs;
var isPasswordVisible = false.obs; final RxBool isPasswordVisible = false.obs;
var password = "".obs; final RxString password = "".obs;
var isSupportedBiometric = false.obs; final RxBool isSupportedBiometric = false.obs;
var biometricType = BiometricTypeEnum.none.obs; final Rx<BiometricTypeEnum> biometricType = BiometricTypeEnum.none.obs;
void Function(String message)? onShowAlertError; void Function(String message)? onShowAlertError;
void Function(String message)? onShowDeviceError; void Function(String message)? onShowDeviceError;
...@@ -52,15 +53,9 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -52,15 +53,9 @@ class LoginViewModel extends RestfulApiViewModel {
Future<void> onLoginPressed(String phone) async { Future<void> onLoginPressed(String phone) async {
if (password.value.isEmpty) return; if (password.value.isEmpty) return;
showLoading(); showLoading();
try {
final result = await _loginService.login(phone, password.value); final result = await _loginService.login(phone, password.value);
hideLoading(); hideLoading();
_handleLoginResult(result, phone); _handleLoginResult(result, phone);
} catch (e) {
hideLoading();
print('Login error: ${e.toString()}');
onShowAlertError?.call(Constants.commonError);
}
} }
/// REFACTORED: Handle login result with proper error handling /// REFACTORED: Handle login result with proper error handling
...@@ -84,6 +79,7 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -84,6 +79,7 @@ class LoginViewModel extends RestfulApiViewModel {
break; break;
case LoginResult.invalidCredentials: case LoginResult.invalidCredentials:
loginState.value = LoginState.error; loginState.value = LoginState.error;
onShowAlertError?.call(result.message ?? Constants.commonError);
break; break;
case LoginResult.networkError: case LoginResult.networkError:
case LoginResult.unknownError: case LoginResult.unknownError:
...@@ -103,24 +99,15 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -103,24 +99,15 @@ class LoginViewModel extends RestfulApiViewModel {
} }
Future<void> onForgotPassPressed(String phone) async { Future<void> onForgotPassPressed(String phone) async {
showLoading(); await callApi<CreateOTPResponseModel>(
try { request: () => client.otpCreateNew(phone),
final response = await client.otpCreateNew(phone); onSuccess: (data, _) {
hideLoading(); Get.to(OtpScreen(repository: ForgotPassOTPRepository(phone, data.resendAfterSecond ?? Constants.otpTtl)));
if (!response.isSuccess) { },
onShowAlertError?.call(response.errorMessage ?? Constants.commonError); onFailure: (msg, _, _) async {
return; onShowAlertError?.call(msg);
} },
Get.to(
OtpScreen(
repository: ForgotPassOTPRepository(phone, response.data?.resendAfterSecond ?? Constants.otpTtl),
),
); );
} catch (e) {
hideLoading();
print('OTP error: ${e.toString()}');
onShowAlertError?.call(Constants.commonError);
}
} }
/// REFACTORED: Biometric login using LoginService /// REFACTORED: Biometric login using LoginService
...@@ -137,14 +124,8 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -137,14 +124,8 @@ class LoginViewModel extends RestfulApiViewModel {
return; return;
} }
showLoading(); showLoading();
try {
final result = await _loginService.biometricLogin(phone); final result = await _loginService.biometricLogin(phone);
hideLoading(); hideLoading();
_handleLoginResult(result, phone); _handleLoginResult(result, phone);
} catch (e) {
hideLoading();
print('Biometric login error: ${e.toString()}');
onShowAlertError?.call(Constants.commonError);
}
} }
} }
...@@ -24,6 +24,10 @@ class _MembershipScreenState extends BaseState<MembershipScreen> with BasicState ...@@ -24,6 +24,10 @@ class _MembershipScreenState extends BaseState<MembershipScreen> with BasicState
void initState() { void initState() {
super.initState(); super.initState();
_viewModel = Get.put(MembershipViewModel()); _viewModel = Get.put(MembershipViewModel());
_viewModel.onShowAlertError = (message) {
if (message.isEmpty) return;
showAlertError(content: message);
};
} }
@override @override
......
...@@ -8,10 +8,10 @@ import 'models/membership_level_model.dart'; ...@@ -8,10 +8,10 @@ import 'models/membership_level_model.dart';
import 'models/membership_level_term_and_condition_model.dart'; import 'models/membership_level_term_and_condition_model.dart';
class MembershipViewModel extends RestfulApiViewModel { class MembershipViewModel extends RestfulApiViewModel {
var isLoading = false.obs; final Rxn<MembershipInfoResponse> membershipInfo = Rxn<MembershipInfoResponse>();
var membershipInfo = Rxn<MembershipInfoResponse>(); final RxInt selectedTab = 0.obs;
var selectedTab = 0.obs;
MembershipLevelModel? selectedLevel; MembershipLevelModel? selectedLevel;
void Function(String message)? onShowAlertError;
List<MembershipLevelModel>? get levels { List<MembershipLevelModel>? get levels {
return membershipInfo.value?.levels; return membershipInfo.value?.levels;
...@@ -50,23 +50,15 @@ class MembershipViewModel extends RestfulApiViewModel { ...@@ -50,23 +50,15 @@ class MembershipViewModel extends RestfulApiViewModel {
} }
Future<void> getMembershipLevelInfo() async { Future<void> getMembershipLevelInfo() async {
showLoading(); await callApi<MembershipInfoResponse>(
try { request: () => client.getMembershipLevelInfo(),
final response = await client.getMembershipLevelInfo(); onSuccess: (data, _) {
if (response.isSuccess && response.data != null) { membershipInfo.value = data;
membershipInfo.value = response.data;
_makeSelectedLevel(); _makeSelectedLevel();
} else { },
if (kDebugMode) { onFailure: (msg, _, _) async {
print("Failed to get membership info: ${response.errorMessage}"); onShowAlertError?.call(msg);
} },
} );
} catch (e) {
if (kDebugMode) {
print("Error fetching membership level info: $e");
}
} finally {
hideLoading();
}
} }
} }
...@@ -4,15 +4,15 @@ import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.da ...@@ -4,15 +4,15 @@ import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.da
import 'package:mypoint_flutter_app/screen/mobile_card/models/product_mobile_card_model.dart'; import 'package:mypoint_flutter_app/screen/mobile_card/models/product_mobile_card_model.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import '../../preference/point/point_manager.dart'; import '../../preference/point/point_manager.dart';
import 'models/mobile_service_redeem_data.dart';
import 'models/usable_voucher_model.dart'; import 'models/usable_voucher_model.dart';
class ProductMobileCardViewModel extends RestfulApiViewModel { class ProductMobileCardViewModel extends RestfulApiViewModel {
void Function(String message)? onShowAlertError; void Function(String message)? onShowAlertError;
void Function(UsableVoucherModel data)? onRedeemProductMobileSuccess; void Function(UsableVoucherModel data)? onRedeemProductMobileSuccess;
final RxMap<String, List<ProductMobileCardModel>> groupedSection = RxMap<String, List<ProductMobileCardModel>>();
RxMap<String, List<ProductMobileCardModel>> groupedSection = RxMap<String, List<ProductMobileCardModel>>(); final RxList<ProductMobileCardModel> mobileCardSections = <ProductMobileCardModel>[].obs;
var mobileCardSections = RxList<ProductMobileCardModel>(); final RxString selectedBrandCode = "".obs;
RxString selectedBrandCode = "".obs;
List<ProductMobileCardModel> get products { List<ProductMobileCardModel> get products {
return groupedSection[selectedBrandCode.value] ?? []; return groupedSection[selectedBrandCode.value] ?? [];
} }
...@@ -32,48 +32,44 @@ class ProductMobileCardViewModel extends RestfulApiViewModel { ...@@ -32,48 +32,44 @@ class ProductMobileCardViewModel extends RestfulApiViewModel {
} }
Future<void> redeemProductMobileCard() async { Future<void> redeemProductMobileCard() async {
showLoading(); await callApi<MobileServiceRedeemData>(
try { request: () => client.redeemMobileCard((selectedProduct?.id ?? 0).toString()),
final response = await client.redeemMobileCard((selectedProduct?.id ?? 0).toString()); onSuccess: (data, _) async {
final itemId = response.data?.itemId ?? ""; final itemId = data.itemId ?? "";
hideLoading();
if (itemId.isEmpty) { if (itemId.isEmpty) {
print("redeemMobileCard failed: ${response.errorMessage}"); onShowAlertError?.call(Constants.commonError);
onShowAlertError?.call(response.errorMessage ?? Constants.commonError);
return;
}
_getMobileCardCode(itemId);
} catch (error) {
hideLoading();
onShowAlertError?.call(error.toString());
return; return;
} }
await _getMobileCardCode(itemId);
},
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
Future<void> _getMobileCardCode(String itemId) async { Future<void> _getMobileCardCode(String itemId) async {
showLoading(); await callApi<RedeemProductResponseModel>(
try { request: () => client.getMobileCardCode(itemId),
final response = await client.getMobileCardCode(itemId); onSuccess: (data, _) {
hideLoading(); final item = data.item;
final data = response.data?.item; if (item != null) {
if (response.isSuccess && data != null) { onRedeemProductMobileSuccess?.call(item);
onRedeemProductMobileSuccess?.call(data); } else {
return; onShowAlertError?.call(Constants.commonError);
} }
onShowAlertError?.call(response.message ?? Constants.commonError); },
} catch (error) { onFailure: (msg, _, _) async {
hideLoading(); onShowAlertError?.call(msg);
onShowAlertError?.call(error.toString()); },
return; );
}
} }
Future<void> getProductMobileCard() async { Future<void> getProductMobileCard() async {
showLoading(); await callApi<ProductMobileCardResponse>(
try { request: () => client.productMobileCardGetList(),
final response = await client.productMobileCardGetList(); onSuccess: (data, _) {
final result = response.data?.products ?? []; final result = data.products ?? [];
final seen = <String>{}; final seen = <String>{};
final uniqueBrandCode = <ProductMobileCardModel>[]; final uniqueBrandCode = <ProductMobileCardModel>[];
for (final p in result) { for (final p in result) {
...@@ -83,24 +79,18 @@ class ProductMobileCardViewModel extends RestfulApiViewModel { ...@@ -83,24 +79,18 @@ class ProductMobileCardViewModel extends RestfulApiViewModel {
} }
} }
selectedBrandCode.value = uniqueBrandCode.isNotEmpty ? uniqueBrandCode.first.brandCode ?? "" : ""; selectedBrandCode.value = uniqueBrandCode.isNotEmpty ? uniqueBrandCode.first.brandCode ?? "" : "";
mobileCardSections.value = uniqueBrandCode; mobileCardSections.assignAll(uniqueBrandCode);
final Map<String, List<ProductMobileCardModel>> grouped = {}; final Map<String, List<ProductMobileCardModel>> grouped = {};
for (final product in result) { for (final product in result) {
final code = product.brandCode ?? 'unknown'; final code = product.brandCode ?? 'unknown';
if (!grouped.containsKey(code)) { grouped.putIfAbsent(code, () => <ProductMobileCardModel>[]).add(product);
grouped[code] = []; }
} groupedSection.assignAll(grouped);
grouped[code]!.add(product); },
} onFailure: (msg, _, _) async {
groupedSection.value = grouped; onShowAlertError?.call(msg);
},
hideLoading(); );
if (!response.isSuccess) {
onShowAlertError?.call(response.message ?? Constants.commonError);
}
} catch (error) {
onShowAlertError?.call(error.toString());
}
} }
} }
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import '../../preference/data_preference.dart';
import '../faqs/faqs_model.dart'; import '../faqs/faqs_model.dart';
class NewsListViewModel extends RestfulApiViewModel { class NewsListViewModel extends RestfulApiViewModel {
String folderUri; String folderUri;
var newsList = <PageItemModel>[].obs; final RxList<PageItemModel> newsList = <PageItemModel>[].obs;
var isLoading = false.obs; final RxBool isLoading = false.obs;
var _canLoadMore = true; var _canLoadMore = true;
int start = 0;
int limit = 20; int limit = 20;
NewsListViewModel({this.folderUri = "TIN-TUC"}); NewsListViewModel({this.folderUri = "TIN-TUC"});
...@@ -23,21 +21,25 @@ class NewsListViewModel extends RestfulApiViewModel { ...@@ -23,21 +21,25 @@ class NewsListViewModel extends RestfulApiViewModel {
Future<void> getNewsList({bool isRefresh = false}) async { Future<void> getNewsList({bool isRefresh = false}) async {
if (isLoading.value) return; if (isLoading.value) return;
if (!isRefresh && !_canLoadMore) return; if (!isRefresh && !_canLoadMore) return;
showLoading();
isLoading(true); isLoading(true);
final body = { final body = {
"folder_uri": folderUri, "folder_uri": folderUri,
"start": isRefresh ? 0 : newsList.length, "start": isRefresh ? 0 : newsList.length,
"limit": limit, "limit": limit,
}; };
client.websiteFolderGetPageList(body).then((value) { await callApi<FAQItemModelResponse>(
hideLoading(); request: () => client.websiteFolderGetPageList(body),
isLoading(false); onSuccess: (data, _) {
_canLoadMore = value.data?.items?.length == limit; _canLoadMore = (data.items?.length ?? 0) == limit;
if (isRefresh) { if (isRefresh) {
newsList.value.clear(); newsList.clear();
}
newsList.addAll(data.items ?? []);
},
withLoading: false,
onComplete: () {
isLoading(false);
} }
newsList.addAll(value.data?.items ?? []); );
});
} }
} }
\ No newline at end of file
...@@ -162,7 +162,7 @@ class _NotificationScreenState extends BaseState<NotificationScreen> with BasicS ...@@ -162,7 +162,7 @@ class _NotificationScreenState extends BaseState<NotificationScreen> with BasicS
_isPopupShown = true; _isPopupShown = true;
} }
_confirmDeleteAllNotifications() { void _confirmDeleteAllNotifications() {
final dataAlert = DataAlertModel( final dataAlert = DataAlertModel(
title: "Xoá tất cả", title: "Xoá tất cả",
description: "Bạn có muốn xoá hết tất cả thông báo không? \nLưu ý: bạn sẽ không thể xem lại thông báo đã xoá", description: "Bạn có muốn xoá hết tất cả thông báo không? \nLưu ý: bạn sẽ không thể xem lại thông báo đã xoá",
......
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import '../../base/base_response_model.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import '../../preference/data_preference.dart'; import '../../preference/data_preference.dart';
import 'models/category_notify_item_model.dart'; import 'models/category_notify_item_model.dart';
import 'models/notification_detail_model.dart';
import 'models/notification_item_model.dart'; import 'models/notification_item_model.dart';
import 'models/notification_list_data_model.dart';
import 'notification_detail_screen.dart'; import 'notification_detail_screen.dart';
class NotificationViewModel extends RestfulApiViewModel { class NotificationViewModel extends RestfulApiViewModel {
var categories = RxList<CategoryNotifyItemModel>(); final RxList<CategoryNotifyItemModel> categories = <CategoryNotifyItemModel>[].obs;
var notifications = RxList<NotificationItemModel>(); final RxList<NotificationItemModel> notifications = <NotificationItemModel>[].obs;
final RxBool isLoading = false.obs; final RxBool isLoading = false.obs;
final _pageLimit = 10; final _pageLimit = 10;
var _notificationIndex = 0; var _notificationIndex = 0;
...@@ -25,15 +28,17 @@ class NotificationViewModel extends RestfulApiViewModel { ...@@ -25,15 +28,17 @@ class NotificationViewModel extends RestfulApiViewModel {
} }
void _fetchCategories() async { void _fetchCategories() async {
showLoading(); await callApi<List<CategoryNotifyItemModel>>(
client.getNotificationCategories().then((value) { request: () => client.getNotificationCategories(),
final results = value.data ?? []; onSuccess: (data, _) {
if (results.isNotEmpty) { if (data.isNotEmpty) data[0].isSelected = true;
results[0].isSelected = true; categories.assignAll(data);
}
categories.value = results;
fetchNotifications(refresh: true); fetchNotifications(refresh: true);
}); },
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
void fetchNotifications({bool refresh = false}) async { void fetchNotifications({bool refresh = false}) async {
...@@ -50,18 +55,26 @@ class NotificationViewModel extends RestfulApiViewModel { ...@@ -50,18 +55,26 @@ class NotificationViewModel extends RestfulApiViewModel {
"limit": _pageLimit, "limit": _pageLimit,
"noti_group_id": selectedCategory?.id ?? "", "noti_group_id": selectedCategory?.id ?? "",
}; };
client.getNotifications(body).then((value) { await callApi<NotificationListDataModel>(
isLoading.value = false; request: () => client.getNotifications(body),
hideLoading(); onSuccess: (data, _) {
final items = value.data?.items ?? []; final items = data.items ?? [];
if (refresh) { if (refresh) {
notifications.value = items; notifications.assignAll(items);
} else { } else {
notifications.addAll(items); notifications.addAll(items);
} }
_notificationIndex += items.length; _notificationIndex += items.length;
_hasMoreData = (value.data?.items?.length ?? 0) == _pageLimit; _hasMoreData = items.length == _pageLimit;
}); },
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
withLoading: refresh,
onComplete: () {
isLoading.value = false;
},
);
} }
void selectCategory(int index) { void selectCategory(int index) {
...@@ -72,38 +85,55 @@ class NotificationViewModel extends RestfulApiViewModel { ...@@ -72,38 +85,55 @@ class NotificationViewModel extends RestfulApiViewModel {
} }
void notificationMarkAsSeen() { void notificationMarkAsSeen() {
client.notificationMarkAsSeen().then((value) { callApi<EmptyCodable>(
_fetchCategories(); request: () => client.notificationMarkAsSeen(),
}); onSuccess: (_, _) => _fetchCategories(),
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
void deleteAllNotifications() { void deleteAllNotifications() {
client.deleteAllNotifications().then((value) { callApi<EmptyCodable>(
_fetchCategories(); request: () => client.deleteAllNotifications(),
}); onSuccess: (_, _) => _fetchCategories(),
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
void handleRemoveNotification(NotificationItemModel item) { void handleRemoveNotification(NotificationItemModel item) {
if (item.notificationId == null) { return; } if (item.notificationId == null) return;
client.deleteNotification(item.notificationId ?? "").then((value) { callApi<EmptyCodable>(
request: () => client.deleteNotification(item.notificationId ?? ""),
onSuccess: (_, _) {
notifications.remove(item); notifications.remove(item);
if (notifications.length <= _pageLimit) { if (notifications.length <= _pageLimit) {
fetchNotifications(refresh: false); fetchNotifications(refresh: false);
} }
}); },
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
void handleClickNotification(NotificationItemModel item) { void handleClickNotification(NotificationItemModel item) {
showLoading(); callApi<NotificationDetailResponseModel>(
client.getNotificationDetail(item.notificationId ?? "").then((value) { request: () => client.getNotificationDetail(item.notificationId ?? ""),
hideLoading(); onSuccess: (data, _) {
if (!value.isSuccess) return; final notification = data.notification;
final notification = value.data?.notification;
if (notification == null) return; if (notification == null) return;
final pushSuccess = notification.directionalScreen?.begin() ?? false; final pushSuccess = notification.directionalScreen?.begin() ?? false;
if (!pushSuccess) { if (!pushSuccess) {
Get.to(() => NotificationDetailScreen(notification: notification)); Get.to(() => NotificationDetailScreen(notification: notification));
} }
}); },
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
} }
...@@ -2,18 +2,13 @@ import 'package:flutter/material.dart'; ...@@ -2,18 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/base/base_response_model.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart'; import 'package:mypoint_flutter_app/shared/router_gage.dart';
import '../../base/base_screen.dart'; import '../../base/base_screen.dart';
import '../../base/basic_state.dart'; import '../../base/basic_state.dart';
import '../../configs/constants.dart';
import '../../resources/base_color.dart'; import '../../resources/base_color.dart';
import '../biometric/biometric_screen.dart';
import '../faqs/faqs_screen.dart'; import '../faqs/faqs_screen.dart';
import '../login/login_screen.dart';
import '../otp/otp_screen.dart'; import '../otp/otp_screen.dart';
import '../otp/verify_otp_repository.dart'; import '../otp/verify_otp_repository.dart';
import '../pageDetail/campaign_detail_screen.dart';
import '../pageDetail/model/detail_page_rule_type.dart'; import '../pageDetail/model/detail_page_rule_type.dart';
import 'model/check_phone_response_model.dart'; import 'model/check_phone_response_model.dart';
import 'onboarding_viewmodel.dart'; import 'onboarding_viewmodel.dart';
...@@ -32,54 +27,16 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState ...@@ -32,54 +27,16 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_viewModel.fetchOnboardingContent(); _viewModel.onShowAlertError = (message) {
_viewModel.checkPhoneRes.listen((response) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
hideKeyboard(); showAlertError(content: message);
_handleResponseCheckPhoneNumber(response.data);
_handleResponseError(response);
});
}); });
} };
_viewModel.fetchOnboardingContent();
void _handleResponseError(BaseResponseModel<CheckPhoneResponseModel>? response) {
if (response?.isSuccess ?? false) return;
var errorCode = response?.errorCode ?? "";
var errorMessage = response?.errorMessage ?? Constants.commonError;
WidgetsBinding.instance.addPostFrameCallback((_) {
showAlertError(content: errorMessage);
if (errorCode == ErrorCodes.deviceLock) {
// show alert error popupErrorCSKH
return;
} //show alert error popupError
});
}
void _handleResponseCheckPhoneNumber(CheckPhoneResponseModel? response) {
if (response == null) return;
if (response.requireRecaptcha == true) return;
if ((response.mfaToken ?? "").isNotEmpty || (response.nextAction == "signup")) {
// show OTP
Get.to(
() => OtpScreen(
repository: VerifyOtpRepository(
_viewModel.phoneNumber.value,
response?.otpTtl ?? 0,
response?.mfaToken ?? "",
),
),
);
return;
}
if (response.nextAction == "login") {
Get.toNamed(loginScreen, arguments: {'phone': _viewModel.phoneNumber.value});
}
} }
@override @override
Widget createBody() { Widget createBody() {
final double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
return GestureDetector( return GestureDetector(
onTap: hideKeyboard, onTap: hideKeyboard,
child: Scaffold( child: Scaffold(
...@@ -149,6 +106,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState ...@@ -149,6 +106,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
onPressed: onPressed:
_viewModel.isButtonEnabled _viewModel.isButtonEnabled
? () { ? () {
hideKeyboard();
_viewModel.checkPhoneNumber(); _viewModel.checkPhoneNumber();
} }
: null, : null,
......
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/extensions/string_extension.dart'; import 'package:mypoint_flutter_app/extensions/string_extension.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import '../../base/base_response_model.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import '../../shared/router_gage.dart';
import '../otp/otp_screen.dart';
import '../otp/verify_otp_repository.dart';
import 'model/check_phone_response_model.dart'; import 'model/check_phone_response_model.dart';
import 'model/onboarding_info_model.dart'; import 'model/onboarding_info_model.dart';
class OnboardingViewModel extends RestfulApiViewModel { class OnboardingViewModel extends RestfulApiViewModel {
var info = BaseResponseModel<OnboardingInfoModel>().obs; final RxString phoneNumber = "".obs;
var checkPhoneRes = BaseResponseModel<CheckPhoneResponseModel>().obs; final RxBool isChecked = true.obs;
var phoneNumber = "".obs; final _info = Rxn<OnboardingInfoModel>();
var isChecked = true.obs; var checkPhoneRes = Rxn<CheckPhoneResponseModel>();
void Function(String message)? onShowAlertError;
bool get isButtonEnabled => isChecked.value && phoneNumber.value.isPhoneValid(); bool get isButtonEnabled => isChecked.value && phoneNumber.value.isPhoneValid();
String get content => info?.value?.data?.content ?? "";
String get url => info?.value?.data?.url ?? ""; String get content => _info?.value?.content ?? "";
String get url => _info?.value?.url ?? "";
void updatePhoneNumber(String value) { void updatePhoneNumber(String value) {
phoneNumber.value = value; phoneNumber.value = value;
...@@ -25,16 +30,35 @@ class OnboardingViewModel extends RestfulApiViewModel { ...@@ -25,16 +30,35 @@ class OnboardingViewModel extends RestfulApiViewModel {
} }
Future<void> fetchOnboardingContent() async { Future<void> fetchOnboardingContent() async {
client.getOnboardingInfo().then((value) { await callApi<OnboardingInfoModel>(
info.value = value; request: () => client.getOnboardingInfo(),
}); onSuccess: (data, _) {
_info.value = data;
},
withLoading: false,
);
} }
Future<void> checkPhoneNumber() async { Future<void> checkPhoneNumber() async {
showLoading(); await callApi<CheckPhoneResponseModel>(
client.checkPhoneNumber(phoneNumber.value).then((value) { request: () => client.checkPhoneNumber(phoneNumber.value),
hideLoading(); onSuccess: (data, _) {
checkPhoneRes.value = value; checkPhoneRes.value = data;
}); if (data.requireRecaptcha == true) return;
final mfaToken = data.mfaToken ?? "";
final nextAction = data.nextAction ?? "";
final otpTtl = data.otpTtl ?? 0;
if (mfaToken.isNotEmpty || (nextAction == "signup")) {
Get.to(() => OtpScreen(repository: VerifyOtpRepository(phoneNumber.value, otpTtl, mfaToken)));
return;
}
if (nextAction == "login") {
Get.toNamed(loginScreen, arguments: {'phone': phoneNumber.value});
}
},
onFailure: (msg, _, _) {
onShowAlertError?.call(msg);
},
);
} }
} }
...@@ -250,7 +250,7 @@ class _SurveyQuestionScreenState extends BaseState<SurveyQuestionScreen> with Ba ...@@ -250,7 +250,7 @@ class _SurveyQuestionScreenState extends BaseState<SurveyQuestionScreen> with Ba
showAlert(data: dataAlert); showAlert(data: dataAlert);
} }
_showAlertConfirmQuitSurvey() { void _showAlertConfirmQuitSurvey() {
final dataAlert = DataAlertModel( final dataAlert = DataAlertModel(
description: "Có vẻ bạn chưa hoàn thành nhiệm vụ rồi. Tiếp tục nhé!!", description: "Có vẻ bạn chưa hoàn thành nhiệm vụ rồi. Tiếp tục nhé!!",
localHeaderImage: "assets/images/ic_pipi_03.png", localHeaderImage: "assets/images/ic_pipi_03.png",
......
...@@ -4,7 +4,7 @@ import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.da ...@@ -4,7 +4,7 @@ import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.da
import '../model/auth/login_token_response_model.dart'; import '../model/auth/login_token_response_model.dart';
import '../networking/restful_api_viewmodel.dart'; import '../networking/restful_api_viewmodel.dart';
import '../preference/data_preference.dart'; import '../preference/data_preference.dart';
import '../networking/app_navigator.dart'; import '../base/app_navigator.dart';
class TokenRefreshService extends RestfulApiViewModel { class TokenRefreshService extends RestfulApiViewModel {
static final TokenRefreshService _instance = TokenRefreshService._internal(); static final TokenRefreshService _instance = TokenRefreshService._internal();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment