Commit 89983084 authored by DatHV's avatar DatHV
Browse files

update logic. refactor code

parent 928c3660
...@@ -58,7 +58,7 @@ class AppLoading { ...@@ -58,7 +58,7 @@ class AppLoading {
} }
void show({ void show({
Duration timeout = const Duration(seconds: Constants.loadingTimeoutSeconds), Duration timeout = const Duration(seconds: Constants.timeoutSeconds),
Color? barrierColor = const Color(0x33000000), Color? barrierColor = const Color(0x33000000),
bool absorbPointers = true, bool absorbPointers = true,
double size = 56, double size = 56,
......
...@@ -37,21 +37,8 @@ class BaseViewModel extends GetxController with WidgetsBindingObserver { ...@@ -37,21 +37,8 @@ class BaseViewModel extends GetxController with WidgetsBindingObserver {
} }
} }
void showProgressIndicator() { void showLoading() {
Get.dialog( AppLoading().show();
const Center(child: CircularProgressIndicator()),
barrierDismissible: false,
);
}
void hideProgressIndicator() {
if (Get.isDialogOpen ?? false) {
Get.back();
}
}
void showLoading({int timeout = Constants.loadingTimeoutSeconds}) {
AppLoading().show(timeout: Duration(seconds: timeout));
} }
void hideLoading() { void hideLoading() {
......
...@@ -4,8 +4,7 @@ class Constants { ...@@ -4,8 +4,7 @@ class Constants {
static var otpTtl = 180; static var otpTtl = 180;
static var directionInApp = "IN-APP"; static var directionInApp = "IN-APP";
static var phoneNumberCount = 10; static var phoneNumberCount = 10;
static var timeoutSeconds = 30; static const timeoutSeconds = 30;
static const loadingTimeoutSeconds = 30;
static const appStoreId = '1495923300'; static const appStoreId = '1495923300';
} }
......
import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
class LoggerInterceptor extends Interceptor { class LoggerInterceptor extends Interceptor {
// Configs
final bool prettyPrintJson = false; // Mặc định: JSON 1 dòng dễ copy/paste
final bool chunkLogging = true; // Chia nhỏ log theo block để tránh bị cắt
final int chunkSize = 800; // Kích thước mỗi block
final Logger _logger = Logger( final Logger _logger = Logger(
printer: PrettyPrinter( printer: PrettyPrinter(
methodCount: 0, methodCount: 0,
...@@ -14,12 +20,17 @@ class LoggerInterceptor extends Interceptor { ...@@ -14,12 +20,17 @@ class LoggerInterceptor extends Interceptor {
@override @override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) { void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final uri = options.uri; final uri = options.uri;
_logger.i( final buffer = StringBuffer();
'🚀 ${options.method} $uri\n' buffer.writeln('🚀 ${options.method} $uri');
'Headers: ${_formatHeaders(options.headers)}\n' buffer.writeln('Headers: ${_formatHeaders(options.headers)}');
'Query: ${options.queryParameters}\n' if (options.queryParameters.isNotEmpty) {
'Body: ${_formatData(options.data)}', buffer.writeln('Query: ${_stringify(options.queryParameters)}');
); }
_emit(Level.info, 'REQUEST', uri.toString(), buffer.toString());
if (options.data != null) {
final bodyString = _stringify(options.data);
_emitJsonBlocks(Level.info, 'REQUEST JSON', uri.toString(), bodyString);
}
handler.next(options); handler.next(options);
} }
...@@ -27,10 +38,15 @@ class LoggerInterceptor extends Interceptor { ...@@ -27,10 +38,15 @@ class LoggerInterceptor extends Interceptor {
void onResponse(Response response, ResponseInterceptorHandler handler) { void onResponse(Response response, ResponseInterceptorHandler handler) {
final uri = response.requestOptions.uri; final uri = response.requestOptions.uri;
final statusCode = response.statusCode; final statusCode = response.statusCode;
_logger.d( final buffer = StringBuffer();
'✅ $statusCode ${response.requestOptions.method} $uri\n' buffer.writeln('✅ $statusCode ${response.requestOptions.method} $uri');
'Response: ${_formatData(response.data)}', if (response.headers.map.isNotEmpty) {
); buffer.writeln('Resp Headers: ${_stringify(response.headers.map)}');
}
_emit(Level.debug, 'RESPONSE', uri.toString(), buffer.toString());
// emit body in copy-friendly blocks
final bodyString = _stringify(response.data);
_emitJsonBlocks(Level.debug, 'RESPONSE JSON', uri.toString(), bodyString);
handler.next(response); handler.next(response);
} }
...@@ -38,11 +54,14 @@ class LoggerInterceptor extends Interceptor { ...@@ -38,11 +54,14 @@ class LoggerInterceptor extends Interceptor {
void onError(DioException err, ErrorInterceptorHandler handler) { void onError(DioException err, ErrorInterceptorHandler handler) {
final uri = err.requestOptions.uri; final uri = err.requestOptions.uri;
final statusCode = err.response?.statusCode ?? 'Unknown'; final statusCode = err.response?.statusCode ?? 'Unknown';
_logger.e( final buffer = StringBuffer();
'❌ $statusCode ${err.requestOptions.method} $uri\n' buffer.writeln('❌ $statusCode ${err.requestOptions.method} $uri');
'Error: ${err.message}\n' buffer.writeln('Error: ${err.message}');
'Response: ${_formatData(err.response?.data)}', _emit(Level.error, 'ERROR', uri.toString(), buffer.toString());
); if (err.response?.data != null) {
final bodyString = _stringify(err.response?.data);
_emitJsonBlocks(Level.error, 'ERROR JSON', uri.toString(), bodyString);
}
handler.next(err); handler.next(err);
} }
...@@ -55,11 +74,47 @@ class LoggerInterceptor extends Interceptor { ...@@ -55,11 +74,47 @@ class LoggerInterceptor extends Interceptor {
return filtered.toString(); return filtered.toString();
} }
String _formatData(dynamic data) { String _stringify(dynamic data) {
if (data == null) return 'null'; if (data == null) return 'null';
if (data is String && data.length > 1000) { if (data is String) return data;
return '${data.substring(0, 1000)}... (truncated)'; try {
if (prettyPrintJson) {
final encoder = const JsonEncoder.withIndent(' ');
return encoder.convert(data);
}
return jsonEncode(data);
} catch (_) {
return data.toString();
}
}
void _emit(Level level, String phase, String uri, String message) {
if (!chunkLogging || message.length <= chunkSize) {
_logger.log(level, '[$phase] $uri\n$message');
return;
}
final total = (message.length / chunkSize).ceil();
var index = 0;
for (var i = 0; i < message.length; i += chunkSize) {
final end = (i + chunkSize < message.length) ? i + chunkSize : message.length;
index += 1;
_logger.log(level, '[$phase PART $index/$total] $uri\n${message.substring(i, end)}');
}
}
void _emitJsonBlocks(Level level, String phase, String uri, String jsonText) {
if (!chunkLogging || jsonText.length <= chunkSize) {
_logger.log(level, '[$phase FULL] $uri');
_logger.log(level, jsonText);
return;
}
final total = (jsonText.length / chunkSize).ceil();
var index = 0;
for (var i = 0; i < jsonText.length; i += chunkSize) {
final end = (i + chunkSize < jsonText.length) ? i + chunkSize : jsonText.length;
index += 1;
_logger.log(level, '[$phase PART $index/$total] $uri');
_logger.log(level, jsonText.substring(i, end));
} }
return data.toString();
} }
} }
\ No newline at end of file
...@@ -5,8 +5,9 @@ import '../../preference/data_preference.dart'; ...@@ -5,8 +5,9 @@ import '../../preference/data_preference.dart';
import '../home/models/achievement_model.dart'; import '../home/models/achievement_model.dart';
class AchievementViewModel extends RestfulApiViewModel { class AchievementViewModel extends RestfulApiViewModel {
bool isPointHunting; final bool isPointHunting;
var achievements = <AchievementModel>[].obs; final RxList<AchievementModel> achievements = <AchievementModel>[].obs;
AchievementViewModel({this.isPointHunting = false}); AchievementViewModel({this.isPointHunting = false});
@override @override
...@@ -23,12 +24,16 @@ class AchievementViewModel extends RestfulApiViewModel { ...@@ -23,12 +24,16 @@ class AchievementViewModel extends RestfulApiViewModel {
"start": 0, "start": 0,
"limit": 1000, "limit": 1000,
}; };
await callApi<AchievementListResponse>( await callApi<AchievementListResponse>(
request: () => client.getAchievementList(body), request: () => client.getAchievementList(body),
onSuccess: (data, _) { onSuccess: (data, _) {
achievements.value = data.achievements ?? []; achievements.assignAll(data?.achievements ?? []);
}, },
showAppNavigatorDialog: true, onFailure: (msg, _, __) async {
achievements.clear();
},
showAppNavigatorDialog: false,
); );
} }
} }
\ No newline at end of file
...@@ -13,7 +13,7 @@ class AffiliateTabViewModel extends RestfulApiViewModel { ...@@ -13,7 +13,7 @@ class AffiliateTabViewModel extends RestfulApiViewModel {
final RxList<AffiliateCategoryModel> allAffiliateCategories = <AffiliateCategoryModel>[].obs; final RxList<AffiliateCategoryModel> allAffiliateCategories = <AffiliateCategoryModel>[].obs;
final RxList<AffiliateProductTopSaleModel> affiliateProducts = <AffiliateProductTopSaleModel>[].obs; final RxList<AffiliateProductTopSaleModel> affiliateProducts = <AffiliateProductTopSaleModel>[].obs;
final RxBool isLoading = false.obs; final RxBool isLoading = false.obs;
var overview = Rxn<CashbackOverviewModel>(); final Rxn<CashbackOverviewModel> overview = Rxn<CashbackOverviewModel>();
void Function((List<AffiliateBrandModel>, String) data)? onShowAffiliateBrandPopup; void Function((List<AffiliateBrandModel>, String) data)? onShowAffiliateBrandPopup;
@override @override
...@@ -34,62 +34,79 @@ class AffiliateTabViewModel extends RestfulApiViewModel { ...@@ -34,62 +34,79 @@ class AffiliateTabViewModel extends RestfulApiViewModel {
} }
Future<void> _getAffiliateBrandGetList() async { Future<void> _getAffiliateBrandGetList() async {
try { await callApi<List<AffiliateBrandModel>>(
final result = await client.affiliateBrandGetList(); request: () => client.affiliateBrandGetList(),
affiliateBrands.value = result.data ?? []; onSuccess: (data, _) {
} catch (error) { affiliateBrands.assignAll(data);
print("Error fetching affiliate brands: $error"); },
} onFailure: (msg, _, __) async {
affiliateBrands.clear();
},
showAppNavigatorDialog: true,
);
} }
Future<void> _getAffiliateCategoryGetList() async { Future<void> _getAffiliateCategoryGetList() async {
try { await callApi<List<AffiliateCategoryModel>>(
final result = await client.affiliateCategoryGetList(); request: () => client.affiliateCategoryGetList(),
final category = AffiliateCategoryModel( onSuccess: (data, _) {
code: AffiliateCategoryType.other, final category = AffiliateCategoryModel(
name: "Khác", code: AffiliateCategoryType.other,
); name: "Khác",
final results = (result.data ?? []); );
allAffiliateCategories.value = results; final results = data;
allAffiliateCategories.assignAll(results);
final data = results.take(7).toList(); final limitedData = results.take(7).toList();
data.add(category); limitedData.add(category);
affiliateCategories.value = data; affiliateCategories.assignAll(limitedData);
} catch (error) { },
print("Error fetching affiliate brands: $error"); onFailure: (msg, _, __) async {
} affiliateCategories.clear();
allAffiliateCategories.clear();
},
showAppNavigatorDialog: true,
);
} }
Future<void> _getAffiliateProductTopSale() async { Future<void> _getAffiliateProductTopSale() async {
try { await callApi<List<AffiliateProductTopSaleModel>>(
final result = await client.affiliateProductTopSale(); request: () => client.affiliateProductTopSale(),
affiliateProducts.value = result.data ?? []; onSuccess: (data, _) {
} catch (error) { affiliateProducts.assignAll(data);
print("Error fetching affiliate brands: $error"); },
} onFailure: (msg, _, __) async {
affiliateProducts.clear();
},
showAppNavigatorDialog: true,
);
} }
Future<void> _getAffiliateOverview() async { Future<void> _getAffiliateOverview() async {
try { await callApi<CashbackOverviewModel>(
final result = await client.getCashBackOverview(); request: () => client.getCashBackOverview(),
overview.value = result.data; onSuccess: (data, _) {
} catch (error) { overview.value = data;
print("Error fetching affiliate brands: $error"); },
} onFailure: (msg, _, __) async {
overview.value = null;
},
showAppNavigatorDialog: true,
);
} }
affiliateBrandGetListBuyCategory(AffiliateCategoryModel category) async { Future<void> affiliateBrandGetListBuyCategory(AffiliateCategoryModel category) async {
showLoading(); await callApi<List<AffiliateBrandModel>>(
try { request: () => client.affiliateBrandGetList(categoryCode: AffiliateCategoryModel.codeToJson(category.code)),
final result = await client.affiliateBrandGetList(categoryCode: AffiliateCategoryModel.codeToJson(category.code)); onSuccess: (data, _) {
hideLoading(); if (data.isNotEmpty) {
final data = result.data ?? []; onShowAffiliateBrandPopup?.call((data, category.name ?? ''));
if (result.isSuccess && data.isNotEmpty) { }
onShowAffiliateBrandPopup?.call((data, category.name ?? '')); },
} onFailure: (msg, _, __) async {
} catch (error) { // Không cần làm gì, error đã được handle bởi callApi
hideLoading(); },
print("Error fetching affiliate brands: $error"); showAppNavigatorDialog: true,
} );
} }
} }
\ No newline at end of file
import 'package:get/get_rx/src/rx_types/rx_types.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 '../../configs/constants.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import 'daily_checkin_models.dart'; import 'daily_checkin_models.dart';
class DailyCheckInViewModel extends RestfulApiViewModel { class DailyCheckInViewModel extends RestfulApiViewModel {
var checkInData = Rxn<CheckInDataModel>(); final Rxn<CheckInDataModel> checkInData = Rxn<CheckInDataModel>();
var submitData = Rxn<SubmitCheckInData>(); final Rxn<SubmitCheckInData> submitData = Rxn<SubmitCheckInData>();
void Function(String message, bool onBack)? onShowAlertError; void Function(String message, bool onBack)? onShowAlertError;
void Function(SubmitCheckInData? data)? submitDataResponse; void Function(SubmitCheckInData? data)? submitDataResponse;
...@@ -17,40 +16,34 @@ class DailyCheckInViewModel extends RestfulApiViewModel { ...@@ -17,40 +16,34 @@ class DailyCheckInViewModel extends RestfulApiViewModel {
} }
@override @override
onInit() { void onInit() {
super.onInit(); super.onInit();
_rewardOpportunityGetList(); _rewardOpportunityGetList();
} }
Future<void> _rewardOpportunityGetList() async { Future<void> _rewardOpportunityGetList() async {
showLoading(); await callApi<CheckInDataModel>(
try { request: () => client.rewardOpportunityGetList(),
final response = await client.rewardOpportunityGetList(); onSuccess: (data, _) {
hideLoading(); checkInData.value = data;
checkInData.value = response.data; },
if (!response.isSuccess) { onFailure: (msg, _, __) async {
onShowAlertError?.call(response.errorMessage ?? Constants.commonError, true); onShowAlertError?.call(msg, true);
} },
} catch (error) { );
hideLoading();
onShowAlertError?.call(Constants.commonError, true);
}
} }
Future<void> submitCheckIn() async { Future<void> submitCheckIn() async {
showLoading(); await callApi<SubmitCheckInData>(
try { request: () => client.submitCheckIn(),
final response = await client.submitCheckIn(); onSuccess: (data, _) {
hideLoading(); submitData.value = data;
submitData.value = response.data; submitDataResponse?.call(data);
submitDataResponse?.call(response.data); _rewardOpportunityGetList(); // Refresh data after successful check-in
_rewardOpportunityGetList(); },
if (!response.isSuccess) { onFailure: (msg, _, __) async {
onShowAlertError?.call(response.errorMessage ?? Constants.commonError, false); onShowAlertError?.call(msg, false);
} },
} catch (error) { );
hideLoading();
onShowAlertError?.call(Constants.commonError, false);
}
} }
} }
\ No newline at end of file
...@@ -6,32 +6,38 @@ import '../../../widgets/alert/popup_data_model.dart'; ...@@ -6,32 +6,38 @@ import '../../../widgets/alert/popup_data_model.dart';
import '../models/game_bundle_item_model.dart'; import '../models/game_bundle_item_model.dart';
class GameCardViewModel extends RestfulApiViewModel { class GameCardViewModel extends RestfulApiViewModel {
var data = Rxn<GameBundleItemModel>(); final Rxn<GameBundleItemModel> data = Rxn<GameBundleItemModel>();
void Function(String message, bool onClose)? onShowAlertError; void Function(String message, bool onClose)? onShowAlertError;
void Function(PopupDataModel popup)? submitGameCardSuccess; void Function(PopupDataModel popup)? submitGameCardSuccess;
void Function()? getGameDetailSuccess; void Function()? getGameDetailSuccess;
Future<void> submitGameCard(String gameId, int itemId) async { Future<void> submitGameCard(String gameId, int itemId) async {
showProgressIndicator(); await callApi<GameBundleItemModel>(
final response = await client.submitGameCard(gameId, itemId.toString()); request: () => client.submitGameCard(gameId, itemId.toString()),
hideProgressIndicator(); onSuccess: (data, _) {
final popupData = response.data?.popup; final popupData = data?.popup;
if (response.isSuccess && popupData != null) { if (popupData != null) {
submitGameCardSuccess?.call(popupData); submitGameCardSuccess?.call(popupData);
} else { } else {
onShowAlertError?.call(response.errorMessage ?? Constants.commonError, false); onShowAlertError?.call(Constants.commonError, false);
} }
},
onFailure: (msg, _, __) async {
onShowAlertError?.call(msg, false);
},
);
} }
Future<void> getGameDetail({String? id}) async { Future<void> getGameDetail({String? id}) async {
showLoading(); await callApi<GameBundleItemModel>(
final response = await client.getGameDetail(id ?? data.value?.id ?? ''); request: () => client.getGameDetail(id ?? data.value?.id ?? ''),
hideLoading(); onSuccess: (data, _) {
if (response.data != null) { this.data.value = data;
data.value = response.data; getGameDetailSuccess?.call();
getGameDetailSuccess?.call(); },
} else { onFailure: (msg, _, __) async {
onShowAlertError?.call(response.errorMessage ?? Constants.commonError, true); onShowAlertError?.call(msg, true);
} },
);
} }
} }
\ No newline at end of file
...@@ -9,9 +9,9 @@ class HealthBookCardDetailViewModel extends RestfulApiViewModel { ...@@ -9,9 +9,9 @@ class HealthBookCardDetailViewModel extends RestfulApiViewModel {
void Function(String message)? onShowAlertError; void Function(String message)? onShowAlertError;
Future<void> getHealthBookCardDetail(String cardId) async { Future<void> getHealthBookCardDetail(String cardId) async {
showProgressIndicator(); showLoading();
final response = await client.getDetailHealthBookCard(cardId); final response = await client.getDetailHealthBookCard(cardId);
showProgressIndicator(); hideLoading();
if (response.isSuccess) { if (response.isSuccess) {
card.value = response.data; card.value = response.data;
} else { } else {
......
...@@ -5,32 +5,27 @@ import 'models/history_point_models.dart'; ...@@ -5,32 +5,27 @@ import 'models/history_point_models.dart';
import 'models/transaction_summary_by_date_model.dart'; import 'models/transaction_summary_by_date_model.dart';
class HistoryPointViewModel extends RestfulApiViewModel { class HistoryPointViewModel extends RestfulApiViewModel {
var historyPoint = Rxn<ListHistoryResponseModel>(); final Rxn<ListHistoryResponseModel> historyPoint = Rxn<ListHistoryResponseModel>();
var transactionSummary = Rxn<TransactionSummaryByDateModel>(); final Rxn<TransactionSummaryByDateModel> transactionSummary = Rxn<TransactionSummaryByDateModel>();
final RxInt selectedTabIndex = 0.obs; final RxInt selectedTabIndex = 0.obs;
DateTime selectedDate = DateTime.now(); DateTime selectedDate = DateTime.now();
@override @override
onInit() { void onInit() {
super.onInit(); super.onInit();
freshData(); freshData();
} }
changeDate(bool prevMonth) { void changeDate(bool prevMonth) {
selectedDate = DateTime(selectedDate.year, selectedDate.month + (prevMonth ? -1 : 1), 1); selectedDate = DateTime(selectedDate.year, selectedDate.month + (prevMonth ? -1 : 1), 1);
freshData(); freshData();
} }
Future<void> freshData() async { Future<void> freshData() async {
showLoading(); await Future.wait<void>([
try { _getTransactionGetSummaryByDate(),
await Future.wait<void>([ _getTransactionSummaryByDateModel(),
_getTransactionGetSummaryByDate(), ], eagerError: false);
_getTransactionSummaryByDateModel(),
], eagerError: false);
} finally {
hideLoading();
}
} }
Future<void> _getTransactionGetSummaryByDate() async { Future<void> _getTransactionGetSummaryByDate() async {
...@@ -39,8 +34,16 @@ class HistoryPointViewModel extends RestfulApiViewModel { ...@@ -39,8 +34,16 @@ class HistoryPointViewModel extends RestfulApiViewModel {
'year': selectedDate.year, 'year': selectedDate.year,
'lang': 'vi', 'lang': 'vi',
}; };
final res = await client.transactionGetSummaryByDate(body);
transactionSummary.value = res.data; await callApi<TransactionSummaryByDateModel>(
request: () => client.transactionGetSummaryByDate(body),
onSuccess: (data, _) {
transactionSummary.value = data;
},
onFailure: (msg, _, __) async {
transactionSummary.value = null;
},
);
} }
Future<void> _getTransactionSummaryByDateModel() async { Future<void> _getTransactionSummaryByDateModel() async {
...@@ -53,7 +56,15 @@ class HistoryPointViewModel extends RestfulApiViewModel { ...@@ -53,7 +56,15 @@ class HistoryPointViewModel extends RestfulApiViewModel {
'start': 0, 'start': 0,
'lang': 'vi', 'lang': 'vi',
}; };
final res = await client.transactionHistoryGetList(body);
historyPoint.value = res.data; await callApi<ListHistoryResponseModel>(
request: () => client.transactionHistoryGetList(body),
onSuccess: (data, _) {
historyPoint.value = data;
},
onFailure: (msg, _, __) async {
historyPoint.value = null;
},
);
} }
} }
\ No newline at end of file
import 'package:get/get_rx/src/rx_types/rx_types.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 '../../base/base_response_model.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import '../../configs/constants.dart';
import 'models/interested_categories_model.dart'; import 'models/interested_categories_model.dart';
class InterestedCategoriesViewModel extends RestfulApiViewModel { class InterestedCategoriesViewModel extends RestfulApiViewModel {
var interestedCategories = Rxn<InterestedCategoriesResponse>(); final Rxn<InterestedCategoriesResponse> interestedCategories = Rxn<InterestedCategoriesResponse>();
Set<String> selectedIds = {}; Set<String> selectedIds = {};
void Function(String message)? onShowAlertError; void Function(String message)? onShowAlertError;
Future<void> getInterestedCategories() async { Future<void> getInterestedCategories() async {
showLoading(); await callApi<InterestedCategoriesResponse>(
try { request: () => client.categoryTopLevelGetList(),
final response = await client.categoryTopLevelGetList(); onSuccess: (data, _) {
hideLoading();
if (response.isSuccess && response.data != null) {
final categories = response.data!;
selectedIds = selectedIds =
categories.listItems data.listItems
?.where((item) => item.subscribed == "1") ?.where((item) => item.subscribed == "1")
.map((item) => item.categoryCode ?? '') .map((item) => item.categoryCode ?? '')
.where((code) => code.isNotEmpty) .where((code) => code.isNotEmpty)
.toList() .toList()
.toSet() ?? .toSet() ??
<String>{}; <String>{};
interestedCategories.value = categories; interestedCategories.value = data;
} else { },
onShowAlertError?.call(response.message ?? Constants.commonError); onFailure: (msg, _, _) async {
} onShowAlertError?.call(msg);
} catch (error) { },
hideLoading(); );
onShowAlertError?.call(Constants.commonError);
}
} }
submitInterestedCategories() async { Future<void> submitInterestedCategories() async {
final categories = selectedIds.toList(); final categories = selectedIds.toList();
showLoading(); await callApi<EmptyCodable>(
try { request: () => client.submitCategorySubscribe(categories.join(',')),
final response = await client.submitCategorySubscribe(categories.join(',')); onSuccess: (data, _) {
hideLoading(); onShowAlertError?.call("Cập nhật sở thích thành công");
onShowAlertError?.call( _handleUnsubscribeCategories(categories);
response.isSuccess ? "Cập nhật sở thích thành công" : response.message ?? Constants.commonError, },
); onFailure: (msg, _, _) async {
final List<String> categoryCodes = onShowAlertError?.call(msg);
interestedCategories.value?.listItems },
?.map((item) => item.categoryCode ?? '') );
.where((code) => code.isNotEmpty) }
.toList() ??
[]; void _handleUnsubscribeCategories(List<String> categories) {
final filteredList = categoryCodes?.where((item) => !categories.contains(item)).toList(); final List<String> categoryCodes = interestedCategories.value?.listItems
if (filteredList == null || filteredList.isEmpty) return; ?.map((item) => item.categoryCode ?? '')
submitUnsubscribeInterestedCategories(filteredList!); .where((code) => code.isNotEmpty)
} catch (error) { .toList() ?? [];
hideLoading();
onShowAlertError?.call(Constants.commonError); final filteredList = categoryCodes.where((item) => !categories.contains(item)).toList();
if (filteredList.isNotEmpty) {
submitUnsubscribeInterestedCategories(filteredList);
} }
} }
submitUnsubscribeInterestedCategories(List<String> categories) async { Future<void> submitUnsubscribeInterestedCategories(List<String> categories) async {
final _ = await client.submitCategoryUnsubscribeList(categories.join(',')); await callApi<EmptyCodable>(
request: () => client.submitCategoryUnsubscribeList(categories.join(',')),
onSuccess: (data, _) {
// Silent success for unsubscribe
},
onFailure: (msg, _, _) async {
// Silent failure for unsubscribe
},
);
} }
} }
...@@ -5,53 +5,55 @@ import '../../networking/restful_api_viewmodel.dart'; ...@@ -5,53 +5,55 @@ import '../../networking/restful_api_viewmodel.dart';
import 'models/invite_friend_campaign_model.dart'; import 'models/invite_friend_campaign_model.dart';
class InviteFriendCampaignViewModel extends RestfulApiViewModel { class InviteFriendCampaignViewModel extends RestfulApiViewModel {
var inviteFriendDetail = Rxn<InviteFriendDetailModel>(); final Rxn<InviteFriendDetailModel> inviteFriendDetail = Rxn<InviteFriendDetailModel>();
var campaignDetail = Rxn<CampaignInviteFriendDetail>(); final Rxn<CampaignInviteFriendDetail> campaignDetail = Rxn<CampaignInviteFriendDetail>();
void Function(String message, bool onBack)? onShowAlertError; void Function(String message, bool onBack)? onShowAlertError;
void Function(String, String)? phoneInviteFriendResponse; void Function(String, String)? phoneInviteFriendResponse;
loadData() { void loadData() {
_getInviteFriendDetail(); _getInviteFriendDetail();
_getCampaignInviteFriendDetail(); _getCampaignInviteFriendDetail();
} }
Future<void> phoneInviteFriend(String phone) async { Future<void> phoneInviteFriend(String phone) async {
showLoading(); await callApi<InviteFriendResponse>(
try { request: () => client.phoneInviteFriend(phone),
final response = await client.phoneInviteFriend(phone); onSuccess: (data, _) {
hideLoading(); final sms = data?.sms ?? '';
final sms = response.data?.sms ?? ''; if (sms.isNotEmpty) {
if (response.isSuccess && sms.isNotEmpty) { phoneInviteFriendResponse?.call(sms, phone);
phoneInviteFriendResponse?.call(sms, phone); } else {
} else { onShowAlertError?.call(Constants.commonError, false);
onShowAlertError?.call(response.errorMessage ?? Constants.commonError, false); }
} },
} catch (error) { onFailure: (msg, _, __) async {
hideLoading(); onShowAlertError?.call(msg, false);
onShowAlertError?.call(Constants.commonError, false); },
} );
} }
Future<void> _getInviteFriendDetail() async { Future<void> _getInviteFriendDetail() async {
showLoading(); await callApi<InviteFriendDetailModel>(
try { request: () => client.getCampaignInviteFriend(),
final response = await client.getCampaignInviteFriend(); onSuccess: (data, _) {
hideLoading(); inviteFriendDetail.value = data;
inviteFriendDetail.value = response.data; },
if (!response.isSuccess) { onFailure: (msg, _, __) async {
onShowAlertError?.call(response.errorMessage ?? Constants.commonError, true); onShowAlertError?.call(msg, true);
} },
} catch (error) { );
onShowAlertError?.call(Constants.commonError, true);
} finally {
hideLoading();
}
} }
Future<void> _getCampaignInviteFriendDetail() async { Future<void> _getCampaignInviteFriendDetail() async {
try { await callApi<CampaignInviteFriendDetail>(
final response = await client.getDetailCampaignInviteFriend(); request: () => client.getDetailCampaignInviteFriend(),
campaignDetail.value = response.data; onSuccess: (data, _) {
} catch (_) {} campaignDetail.value = data;
},
onFailure: (msg, _, __) async {
// Silent failure for this optional call
},
showAppNavigatorDialog: false,
);
} }
} }
...@@ -4,9 +4,7 @@ import '../../networking/restful_api_viewmodel.dart'; ...@@ -4,9 +4,7 @@ import '../../networking/restful_api_viewmodel.dart';
import '../home/models/pipi_detail_model.dart'; import '../home/models/pipi_detail_model.dart';
class PipiDetailViewModel extends RestfulApiViewModel { class PipiDetailViewModel extends RestfulApiViewModel {
var items = RxList<PipiSupportItemModel>(); final RxList<PipiSupportItemModel> items = <PipiSupportItemModel>[].obs;
PipiDetailViewModel();
@override @override
void onInit() { void onInit() {
...@@ -15,11 +13,14 @@ class PipiDetailViewModel extends RestfulApiViewModel { ...@@ -15,11 +13,14 @@ class PipiDetailViewModel extends RestfulApiViewModel {
} }
Future<void> fetchPipiDetails() async { Future<void> fetchPipiDetails() async {
try { await callApi<PipiDetailResponseModel>(
final response = await client.getPipiDetail(); request: () => client.getPipiDetail(),
items.value = response.data?.items ?? []; onSuccess: (data, _) {
} catch (error) { items.assignAll(data?.items ?? []);
print("Error fetching Pipi details: $error"); },
} onFailure: (msg, _, __) async {
items.clear();
},
);
} }
} }
\ No newline at end of file
...@@ -208,8 +208,7 @@ class _RegisterFormInputScreenState extends BaseState<RegisterFormInputScreen> w ...@@ -208,8 +208,7 @@ class _RegisterFormInputScreenState extends BaseState<RegisterFormInputScreen> w
} }
void _continueToPaymentHandle() { void _continueToPaymentHandle() {
final verifyURL = _viewModel.form.value?.verify?.url ?? ''; if (_viewModel.verifyUrl.isNotEmpty) {
if (verifyURL.isNotEmpty) {
_viewModel.verifyRegisterForm(); _viewModel.verifyRegisterForm();
} else { } else {
_gotoPaymentScreen(); _gotoPaymentScreen();
......
...@@ -12,6 +12,9 @@ class RegisterFormInputViewModel extends RestfulApiViewModel { ...@@ -12,6 +12,9 @@ class RegisterFormInputViewModel extends RestfulApiViewModel {
void Function(String message)? onShowAlertError; void Function(String message)? onShowAlertError;
void Function(VerifyRegisterCampaignModel data)? verifyRegisterFormSuccess; void Function(VerifyRegisterCampaignModel data)? verifyRegisterFormSuccess;
String get verifyUrl {
return form.value?.verify?.url ?? '';
}
Future<void> fetchRegisterFormInput(String id) async { Future<void> fetchRegisterFormInput(String id) async {
final response = await client.getRegistrationForm(id); final response = await client.getRegistrationForm(id);
form.value = response.data; form.value = response.data;
...@@ -21,11 +24,10 @@ class RegisterFormInputViewModel extends RestfulApiViewModel { ...@@ -21,11 +24,10 @@ class RegisterFormInputViewModel extends RestfulApiViewModel {
} }
Future<void> verifyRegisterForm() async { Future<void> verifyRegisterForm() async {
showProgressIndicator(); showLoading();
final path = form.value?.formRegistration?.verify?.url ?? '/accountPasswordReset/1.0.0';
final metaData = (form.value?.submitParams ?? {}).toJsonString(); final metaData = (form.value?.submitParams ?? {}).toJsonString();
final response = await client.verifyRegisterForm(path, {'metadata': metaData}); final response = await client.verifyRegisterForm(verifyUrl, {'metadata': metaData});
hideProgressIndicator(); hideLoading();
final data = response.data; final data = response.data;
if (!response.isSuccess && data != null) { if (!response.isSuccess && data != null) {
onShowAlertError?.call(response.errorMessage ?? Constants.commonError); onShowAlertError?.call(response.errorMessage ?? Constants.commonError);
......
...@@ -49,13 +49,13 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w ...@@ -49,13 +49,13 @@ class _TransactionDetailScreenState extends BaseState<TransactionDetailScreen> w
_viewModel = Get.put( _viewModel = Get.put(
TransactionDetailViewModel(product: _product!, quantity: _quantity, targetPhoneNumber: _targetPhoneNumber, metaData: _metaData), TransactionDetailViewModel(product: _product!, quantity: _quantity, targetPhoneNumber: _targetPhoneNumber, metaData: _metaData),
); );
_viewModel.refreshData();
_viewModel.onShowAlertError = (message) { _viewModel.onShowAlertError = (message) {
if (message.isNotEmpty) { if (message.isEmpty) return;
WidgetsBinding.instance.addPostFrameCallback((_) {
showAlertError(content: message); showAlertError(content: message);
} });
}; };
_viewModel.refreshData();
} }
@override @override
......
...@@ -39,12 +39,6 @@ class TransactionDetailViewModel extends RestfulApiViewModel { ...@@ -39,12 +39,6 @@ class TransactionDetailViewModel extends RestfulApiViewModel {
TransactionDetailViewModel({required this.product, required this.quantity, this.targetPhoneNumber, this.metaData}); TransactionDetailViewModel({required this.product, required this.quantity, this.targetPhoneNumber, this.metaData});
@override
void onInit() {
super.onInit();
refreshData();
}
Future<void> refreshData() async { Future<void> refreshData() async {
isLoading.value = true; isLoading.value = true;
await Future.wait([_getPreviewOrderPayment(), _getPaymentMethods(), _getPaymentBankAccounts()]); await Future.wait([_getPreviewOrderPayment(), _getPaymentMethods(), _getPaymentBankAccounts()]);
......
...@@ -43,7 +43,7 @@ class MyMobileCardDetailViewModel extends RestfulApiViewModel { ...@@ -43,7 +43,7 @@ class MyMobileCardDetailViewModel extends RestfulApiViewModel {
Future<void> onChangeCardStatus() async { Future<void> onChangeCardStatus() async {
final newState = !isUsed.value; final newState = !isUsed.value;
showProgressIndicator(); showLoading();
try { try {
final response = newState ? await client.myProductMarkAsUsed(itemId) : await client.myProductMarkAsNotUsedYet(itemId); final response = newState ? await client.myProductMarkAsUsed(itemId) : await client.myProductMarkAsNotUsedYet(itemId);
if (response.isSuccess) { if (response.isSuccess) {
...@@ -52,7 +52,7 @@ class MyMobileCardDetailViewModel extends RestfulApiViewModel { ...@@ -52,7 +52,7 @@ class MyMobileCardDetailViewModel extends RestfulApiViewModel {
} catch (_) { } catch (_) {
} finally { } finally {
hideProgressIndicator(); hideLoading();
} }
} }
} }
\ No newline at end of file
...@@ -2,6 +2,7 @@ import 'package:barcode_widget/barcode_widget.dart'; ...@@ -2,6 +2,7 @@ import 'package:barcode_widget/barcode_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:mypoint_flutter_app/widgets/back_button.dart'; import 'package:mypoint_flutter_app/widgets/back_button.dart';
import 'package:mypoint_flutter_app/widgets/custom_toast_message.dart';
import 'package:mypoint_flutter_app/widgets/dashed_line.dart'; import 'package:mypoint_flutter_app/widgets/dashed_line.dart';
import 'package:qr_flutter/qr_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart';
import '../../resources/base_color.dart'; import '../../resources/base_color.dart';
...@@ -80,7 +81,7 @@ class VoucherCodeCardScreen extends StatelessWidget { ...@@ -80,7 +81,7 @@ class VoucherCodeCardScreen extends StatelessWidget {
GestureDetector( GestureDetector(
onTap: () { onTap: () {
Clipboard.setData(ClipboardData(text: code)); Clipboard.setData(ClipboardData(text: code));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Đã sao chép mã'))); showToastMessage('Đã sao chép mã');
}, },
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
......
...@@ -133,9 +133,9 @@ class VoucherListViewModel extends RestfulApiViewModel { ...@@ -133,9 +133,9 @@ class VoucherListViewModel extends RestfulApiViewModel {
} }
void submitCampaignViewVoucherComplete() async { void submitCampaignViewVoucherComplete() async {
showProgressIndicator(); showLoading();
final response = await client.submitCampaignViewVoucherComplete(); final response = await client.submitCampaignViewVoucherComplete();
hideProgressIndicator(); hideLoading();
submitCampaignViewVoucherResponse?.call(response); submitCampaignViewVoucherResponse?.call(response);
} }
} }
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