Commit e9cf8244 authored by DatHV's avatar DatHV
Browse files

update fix bug, handle error.

parent a0bcdab2
...@@ -14,6 +14,7 @@ import '../directional/directional_screen.dart'; ...@@ -14,6 +14,7 @@ import '../directional/directional_screen.dart';
import '../model/auth/biometric_register_response_model.dart'; import '../model/auth/biometric_register_response_model.dart';
import '../model/auth/login_token_response_model.dart'; import '../model/auth/login_token_response_model.dart';
import '../model/auth/profile_response_model.dart'; import '../model/auth/profile_response_model.dart';
import '../preference/package_info.dart';
import '../screen/health_book/health_book_model.dart'; import '../screen/health_book/health_book_model.dart';
import '../screen/history_point/models/history_point_models.dart'; import '../screen/history_point/models/history_point_models.dart';
import '../screen/history_point/models/transaction_summary_by_date_model.dart'; import '../screen/history_point/models/transaction_summary_by_date_model.dart';
...@@ -82,9 +83,10 @@ import '../screen/voucher/models/search_product_response_model.dart'; ...@@ -82,9 +83,10 @@ import '../screen/voucher/models/search_product_response_model.dart';
extension RestfulAPIClientAllRequest on RestfulAPIClient { extension RestfulAPIClientAllRequest on RestfulAPIClient {
Future<BaseResponseModel<UpdateResponseModel>> checkUpdateApp() async { Future<BaseResponseModel<UpdateResponseModel>> checkUpdateApp() async {
final operatingSystem = kIsWeb ? "web" : Platform.operatingSystem; final operatingSystem = Platform.operatingSystem;
final version = kIsWeb ? "1.0.0" : Platform.version; final version = await AppInfoHelper.version;
final body = {"operating_system": operatingSystem, "software_model": "MyPoint", "version": version, "build_number": "1"}; final buildNumber = await AppInfoHelper.buildNumber;
final body = {"operating_system": operatingSystem, "software_model": "MyPoint", "version": version, "build_number": buildNumber};
return requestNormal(APIPaths.checkUpdate, Method.POST, body, (data) => UpdateResponseModel.fromJson(data as Json)); return requestNormal(APIPaths.checkUpdate, Method.POST, body, (data) => UpdateResponseModel.fromJson(data as Json));
} }
......
...@@ -5,13 +5,20 @@ import '../base/base_response_model.dart'; ...@@ -5,13 +5,20 @@ import '../base/base_response_model.dart';
import '../base/base_view_model.dart'; import '../base/base_view_model.dart';
import '../configs/constants.dart'; import '../configs/constants.dart';
import '../base/app_navigator.dart'; import '../base/app_navigator.dart';
import 'dio_extra_keys.dart';
import 'dio_http_service.dart'; import 'dio_http_service.dart';
import 'error_mapper.dart'; import 'error_mapper.dart';
import 'interceptor/network_error_gate.dart'; import 'interceptor/network_error_gate.dart';
typedef ApiCall<T> = Future<BaseResponseModel<T>> Function(); typedef ApiCall<T> = Future<BaseResponseModel<T>> Function();
typedef OnSuccess<T> = FutureOr<void> Function(T data, BaseResponseModel<T> res); typedef OnSuccess<T> =
typedef OnFailure<T> = FutureOr<void> Function(String message, BaseResponseModel<T>? res, Object? error); FutureOr<void> Function(T data, BaseResponseModel<T> res);
typedef OnFailure<T> =
FutureOr<void> Function(
String message,
BaseResponseModel<T>? res,
Object? error,
);
typedef OnComplete = FutureOr<void> Function(); typedef OnComplete = FutureOr<void> Function();
typedef ApiTask = Future<void> Function(); typedef ApiTask = Future<void> Function();
...@@ -39,43 +46,47 @@ class RestfulApiViewModel extends BaseViewModel { ...@@ -39,43 +46,47 @@ class RestfulApiViewModel extends BaseViewModel {
final msg = res.errorMessage ?? defaultError; final msg = res.errorMessage ?? defaultError;
final hasInternet = await NetworkConnectivity().hasInternet(); final hasInternet = await NetworkConnectivity().hasInternet();
if (showAppNavigatorDialog) { if (showAppNavigatorDialog) {
AppNavigator.showAlertError(content: hasInternet ? msg : ErrorCodes.networkError); AppNavigator.showAlertError(
content: hasInternet ? msg : ErrorCodes.networkError,
);
} else { } else {
await onFailure?.call(hasInternet ? msg : ErrorCodes.networkError, res, null); await onFailure?.call(
hasInternet ? msg : ErrorCodes.networkError,
res,
null,
);
} }
} }
} catch (e) { } catch (e) {
if (e is DioException &&
e.requestOptions.extra[kExtraSkipApiErrorHandling] == true) {
return;
}
String msg = defaultError; String msg = defaultError;
if (e is DioException) { if (e is DioException) {
final mapped = e.requestOptions.extra['mapped_error']; final mapped = e.requestOptions.extra['mapped_error'];
msg = (mapped is String && mapped.isNotEmpty) ? mapped : ErrorMapper.map(e); msg =
(mapped is String && mapped.isNotEmpty)
? mapped
: ErrorMapper.map(e);
} else { } else {
msg = ErrorMapper.map(e); msg = ErrorMapper.map(e);
} }
final hasInternet = await NetworkConnectivity().hasInternet(); final hasInternet = await NetworkConnectivity().hasInternet();
if (showAppNavigatorDialog) { if (showAppNavigatorDialog) {
AppNavigator.showAlertError(content: hasInternet ? msg : ErrorCodes.networkError); AppNavigator.showAlertError(
content: hasInternet ? msg : ErrorCodes.networkError,
);
} else { } else {
await onFailure?.call(hasInternet ? msg : ErrorCodes.networkError, res, null); await onFailure?.call(
hasInternet ? msg : ErrorCodes.networkError,
res,
null,
);
} }
} finally { } finally {
if (withLoading) hideLoading(); if (withLoading) hideLoading();
onComplete?.call(); onComplete?.call();
} }
} }
Future<void> runAllApis({
required List<ApiTask> tasks,
OnComplete? onComplete,
bool withLoading = true,
}) async {
if (withLoading) showLoading();
try {
final futures = tasks.map((t) => t()).toList();
await Future.wait(futures);
} finally {
if (withLoading) hideLoading();
onComplete?.call();
}
}
} }
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_client_all_request.dart';
import '../../networking/restful_api_viewmodel.dart';
import '../data_preference.dart';
import 'header_home_model.dart';
class UserPointManager extends RestfulApiViewModel { import '../../screen/home/header_home_viewmodel.dart';
static final UserPointManager _instance = UserPointManager._internal();
class UserPointManager {
UserPointManager._();
static final UserPointManager _instance = UserPointManager._();
factory UserPointManager() => _instance; factory UserPointManager() => _instance;
UserPointManager._internal();
final RxInt _userPoint = 0.obs; final HeaderHomeRepository _repository = HeaderHomeRepository();
HeaderHomeModel? _headerInfo; final RxInt _point = 0.obs;
RxInt get pointStream => _point;
int get point => _point.value;
int get point => _userPoint.value; void setPoint(int value) {
if (point == value) return;
_point.value = value;
}
Future<int?> fetchUserPoint() async { Future<int?> fetchUserPoint({bool withLoading = false}) async {
if (!DataPreference.instance.logged) return null; await _repository.fetchHeader(withLoading: withLoading);
try { return _point.value;
final response = await client.getHomeHeaderData();
if (response.isSuccess && response.data != null) {
_headerInfo = response.data;
_userPoint.value = _headerInfo?.totalPointActive ?? 0;
return _userPoint.value;
} else {
_userPoint.value = 0;
return null;
}
} catch (e) {
_userPoint.value = 0;
return null;
}
} }
} }
...@@ -140,12 +140,11 @@ class _AffiliateBrandDetailScreenState extends BaseState<AffiliateBrandDetailScr ...@@ -140,12 +140,11 @@ class _AffiliateBrandDetailScreenState extends BaseState<AffiliateBrandDetailScr
return Stack( return Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
loadNetworkImage( Image.asset(
url: _viewModel.brandDetailData.value?.background, 'assets/images/bg_header_detail_brand.png',
fit: BoxFit.cover, fit: BoxFit.cover,
height: imageHeight, height: imageHeight,
width: double.infinity, width: double.infinity,
placeholderAsset: 'assets/images/bg_header_detail_brand.png',
), ),
Positioned( Positioned(
left: 0, left: 0,
......
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/widgets/image_loader.dart'; import 'package:mypoint_flutter_app/widgets/image_loader.dart';
...@@ -56,14 +58,15 @@ class _GameTabScreenState extends BaseState<GameTabScreen> with BasicState, Popu ...@@ -56,14 +58,15 @@ class _GameTabScreenState extends BaseState<GameTabScreen> with BasicState, Popu
title: "Games", title: "Games",
leftButtons: _canBackButton ? [CustomBackButton()] : [], leftButtons: _canBackButton ? [CustomBackButton()] : [],
rightButtons: [ rightButtons: [
CompositedTransformTarget( if (Platform.isIOS)
link: _layerLink, CompositedTransformTarget(
child: IconButton( link: _layerLink,
key: _infoKey, child: IconButton(
icon: const Icon(Icons.info, color: Colors.white), key: _infoKey,
onPressed: _togglePopup, icon: const Icon(Icons.info, color: Colors.white),
onPressed: _togglePopup,
),
), ),
),
], ],
), ),
body: Obx(() { body: Obx(() {
......
...@@ -122,3 +122,5 @@ class HealthBookItem extends StatelessWidget { ...@@ -122,3 +122,5 @@ class HealthBookItem extends StatelessWidget {
...@@ -111,7 +111,7 @@ class _HistoryPointScreenState extends State<HistoryPointScreen> { ...@@ -111,7 +111,7 @@ class _HistoryPointScreenState extends State<HistoryPointScreen> {
); );
} }
_showDatePicker() { void _showDatePicker() {
showMonthPicker(context: context, initialDate: _viewModel.selectedDate, lastDate: DateTime.now()).then((date) { showMonthPicker(context: context, initialDate: _viewModel.selectedDate, lastDate: DateTime.now()).then((date) {
if (date == null) return; if (date == null) return;
_viewModel.selectedDate = date; _viewModel.selectedDate = date;
...@@ -202,7 +202,7 @@ class _HistoryPointScreenState extends State<HistoryPointScreen> { ...@@ -202,7 +202,7 @@ class _HistoryPointScreenState extends State<HistoryPointScreen> {
final adjustTotal = item.adjustTotal?.toInt() ?? 0; final adjustTotal = item.adjustTotal?.toInt() ?? 0;
final value = rewardTotal - redeemTotal + adjustTotal; final value = rewardTotal - redeemTotal + adjustTotal;
final valueColor = value >= 0 ? const Color(0xFF21C777) : const Color(0xFFFE515A); final valueColor = value >= 0 ? const Color(0xFF21C777) : const Color(0xFFFE515A);
final valueText = '${value > 0 ? '+' : (value < 0 ? '-' : '')}${value.money(CurrencyUnit.noneSpace)}'; final valueText = '${value > 0 ? '+' : (value < 0 ? '-' : '')}${value.abs().money(CurrencyUnit.noneSpace)}';
final dateText = item.transactionDatetime?.toDate()?.toFormattedString(format: DateFormat.viFull); final dateText = item.transactionDatetime?.toDate()?.toFormattedString(format: DateFormat.viFull);
final transactionId = item.transactionSequenceId.orIfBlank(''); final transactionId = item.transactionSequenceId.orIfBlank('');
return InkWell( return InkWell(
......
...@@ -4,6 +4,7 @@ import 'package:mypoint_flutter_app/extensions/num_extension.dart'; ...@@ -4,6 +4,7 @@ import 'package:mypoint_flutter_app/extensions/num_extension.dart';
import 'package:mypoint_flutter_app/widgets/image_loader.dart'; import 'package:mypoint_flutter_app/widgets/image_loader.dart';
import '../../../preference/data_preference.dart'; import '../../../preference/data_preference.dart';
import '../../../preference/point/header_home_model.dart'; import '../../../preference/point/header_home_model.dart';
import '../../../preference/point/point_manager.dart';
import '../../../shared/router_gage.dart'; import '../../../shared/router_gage.dart';
import '../models/notification_unread_model.dart'; import '../models/notification_unread_model.dart';
...@@ -107,11 +108,14 @@ class HomeGreetingHeader extends StatelessWidget { ...@@ -107,11 +108,14 @@ class HomeGreetingHeader extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
_buildStatItem( Obx(() {
icon: "assets/images/ic_point_gray.png", final point = UserPointManager().pointStream.value;
value: (dataHeader.totalPointActive ?? 0).money(CurrencyUnit.none),// .toString(), return _buildStatItem(
onTap: _onPointTap, icon: "assets/images/ic_point_gray.png",
), value: point.money(CurrencyUnit.none),
onTap: _onPointTap,
);
}),
const SizedBox(width: 8), const SizedBox(width: 8),
_buildStatItem( _buildStatItem(
icon: "assets/images/ic_voucher_gray.png", icon: "assets/images/ic_voucher_gray.png",
......
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 'package:mypoint_flutter_app/preference/data_preference.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import '../../preference/point/point_manager.dart';
import '../../preference/point/header_home_model.dart'; import '../../preference/point/header_home_model.dart';
import 'models/notification_unread_model.dart'; import 'models/notification_unread_model.dart';
class HeaderThemeController extends GetxController { class HeaderHomeRepository extends RestfulApiViewModel {
final background = RxnString(); HeaderHomeRepository._();
void setBackground(String? url) => background.value = url; static final HeaderHomeRepository _instance = HeaderHomeRepository._();
} factory HeaderHomeRepository() => _instance;
class HeaderHomeViewModel extends RestfulApiViewModel { final Rx<HeaderHomeModel?> _headerHome = Rx<HeaderHomeModel?>(null);
final Rx<HeaderHomeModel?> _headerHomeData = Rx<HeaderHomeModel?>(null); final Rxn<NotificationUnreadData> _notificationUnread = Rxn<NotificationUnreadData>();
final Rxn<NotificationUnreadData> notificationUnreadData = Rxn<NotificationUnreadData>(); int get totalPoint => header.totalPointActive ?? 0;
HeaderHomeModel get headerData {
return _headerHomeData.value ??
HeaderHomeModel(
greeting: 'Xin chào!',
totalVoucher: 0,
totalPointActive: 0,
background: '',
);
}
Future<void> freshData() async { HeaderHomeModel get header => _headerHome.value ??
await _getDynamicHeaderHome(); HeaderHomeModel(
await _getNotificationUnread(); greeting: 'Xin chào!',
totalVoucher: 0,
totalPointActive: 0,
background: '',
);
Rx<HeaderHomeModel?> get headerStream => _headerHome;
Rxn<NotificationUnreadData> get notificationStream => _notificationUnread;
Future<void> _load({bool withLoading = false}) async {
await Future.wait([
fetchHeader(withLoading: withLoading),
_fetchNotificationUnread(),
]);
} }
Future<void> _getDynamicHeaderHome() async { Future<void> fetchHeader({bool withLoading = false}) async {
if (!DataPreference.instance.logged) return;
await callApi<HeaderHomeModel>( await callApi<HeaderHomeModel>(
request: () => client.getDynamicHeaderHome(), request: () => client.getDynamicHeaderHome(),
onSuccess: (data, _) { onSuccess: (data, _) {
_headerHomeData.value = data; _headerHome.value = data;
Get.find<HeaderThemeController>().setBackground(_headerHomeData.value?.background); UserPointManager().setPoint(data.totalPointActive ?? 0);
}, },
withLoading: false, withLoading: withLoading,
); );
} }
Future<void> _getNotificationUnread() async { Future<void> _fetchNotificationUnread() async {
if (!DataPreference.instance.logged) return;
await callApi<NotificationUnreadData>( await callApi<NotificationUnreadData>(
request: () => client.getNotificationUnread(), request: () => client.getNotificationUnread(),
onSuccess: (data, _) { onSuccess: (data, _) {
notificationUnreadData.value = data; _notificationUnread.value = data;
}, },
withLoading: false, withLoading: false,
); );
} }
} }
class HeaderThemeController extends GetxController {
final background = RxnString();
void setBackground(String? url) => background.value = url;
}
class HeaderHomeViewModel extends GetxController {
final HeaderHomeRepository _repository = HeaderHomeRepository();
HeaderHomeModel get headerData => _repository.header;
Rxn<NotificationUnreadData> get notificationUnreadData => _repository.notificationStream;
@override
void onInit() {
super.onInit();
ever<HeaderHomeModel?>(_repository.headerStream, (data) {
if (data?.background != null) {
Get.find<HeaderThemeController>().setBackground(data?.background);
}
});
}
Future<void> freshData() => _repository._load(withLoading: false);
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/preference/point/point_manager.dart';
import 'package:mypoint_flutter_app/screen/home/custom_widget/header_home_widget.dart'; import 'package:mypoint_flutter_app/screen/home/custom_widget/header_home_widget.dart';
import 'package:mypoint_flutter_app/screen/home/custom_widget/product_grid_widget.dart'; import 'package:mypoint_flutter_app/screen/home/custom_widget/product_grid_widget.dart';
import 'package:mypoint_flutter_app/screen/pipi/pipi_detail_screen.dart'; import 'package:mypoint_flutter_app/screen/pipi/pipi_detail_screen.dart';
import 'package:mypoint_flutter_app/screen/voucher/models/product_model.dart'; import 'package:mypoint_flutter_app/screen/voucher/models/product_model.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart'; import 'package:mypoint_flutter_app/shared/router_gage.dart';
import '../../directional/directional_action_type.dart'; import '../../directional/directional_action_type.dart';
import '../../widgets/custom_empty_widget.dart';
import '../popup_manager/popup_runner_helper.dart'; import '../popup_manager/popup_runner_helper.dart';
import 'custom_widget/achievement_carousel_widget.dart'; import 'custom_widget/achievement_carousel_widget.dart';
import 'custom_widget/affiliate_brand_grid_widget.dart'; import 'custom_widget/affiliate_brand_grid_widget.dart';
...@@ -19,6 +21,7 @@ import 'custom_widget/news_carousel_widget.dart'; ...@@ -19,6 +21,7 @@ import 'custom_widget/news_carousel_widget.dart';
import 'header_home_viewmodel.dart'; import 'header_home_viewmodel.dart';
import 'home_tab_viewmodel.dart'; import 'home_tab_viewmodel.dart';
import 'models/header_section_type.dart'; import 'models/header_section_type.dart';
import 'models/main_section_config_model.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
const HomeScreen({super.key}); const HomeScreen({super.key});
...@@ -35,6 +38,7 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit { ...@@ -35,6 +38,7 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
UserPointManager().fetchUserPoint();
_headerHomeVM.freshData(); _headerHomeVM.freshData();
runPopupCheck(DirectionalScreenName.home); runPopupCheck(DirectionalScreenName.home);
} }
...@@ -53,137 +57,92 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit { ...@@ -53,137 +57,92 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit {
} }
List<Widget> _buildSectionContent() { List<Widget> _buildSectionContent() {
final List<Widget> sections = []; final sections = _viewModel.sectionLayouts
for (var section in _viewModel.sectionLayouts.value) { .map(_buildSection)
switch (section.headerSectionType) { .whereType<Widget>()
case HeaderSectionType.banner: .toList();
if (_viewModel.banners.isNotEmpty) { if (sections.isEmpty) {
sections.add( return const [Padding(padding: EdgeInsets.symmetric(vertical: 40), child: EmptyWidget())];
BannerCarousel(
banners: _viewModel.banners,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.banner),
onTap: (item) {
item.directionalScreen?.begin();
},
),
);
}
break;
case HeaderSectionType.topButton:
if (_viewModel.services.isNotEmpty) {
sections.add(
MainServiceGrid(
services: _viewModel.services,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.topButton),
onTap: (item) {
item.directionalScreen?.begin();
},
),
);
}
break;
case HeaderSectionType.campaign:
if (_viewModel.achievements.isNotEmpty) {
sections.add(
AchievementCarousel(
items: _viewModel.achievements,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.campaign),
onTap: (item) {
item.directionScreen?.begin();
},
),
);
}
break;
case HeaderSectionType.product:
if (_viewModel.products.isNotEmpty) {
List<ProductModel> products = _viewModel.products;
final length = products.length;
products = (length.isOdd) ? products.sublist(0, length - 1) : products;
sections.add(
ProductGrid(
products: products,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.product),
onTap: (product) {
Get.toNamed(voucherDetailScreen, arguments: product.id);
},
),
);
}
break;
case HeaderSectionType.news:
if (_viewModel.news.isNotEmpty) {
sections.add(
NewsCarouselWidget(
items: _viewModel.news,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.news),
onTap: (item) async {
Get.toNamed(campaignDetailScreen, arguments: {"id": item.pageId});
},
),
);
}
break;
case HeaderSectionType.myProduct:
if (_viewModel.myProducts.isNotEmpty) {
sections.add(
MyProductCarouselWidget(
items: _viewModel.myProducts,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.myProduct),
onTap: (item) async {
Get.toNamed(voucherDetailScreen, arguments: {"customerProductId": item.id});
},
),
);
}
break;
case HeaderSectionType.flashSale:
final products = _viewModel.flashSaleData.value?.products ?? [];
if (products.isNotEmpty) {
sections.add(
FlashSaleCarouselWidget(
products: products,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.flashSale),
onTap: (product) {
Get.toNamed(voucherDetailScreen, arguments: product.id);
},
),
);
}
break;
case HeaderSectionType.brand:
if (_viewModel.brands.isNotEmpty) {
sections.add(
BrandGridWidget(
brands: _viewModel.brands,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.brand),
onTap: (item) {
// Get.toNamed(affiliateDetailScreen, arguments: item.brandId);
},
),
);
}
break;
case HeaderSectionType.pointPartner:
if (_viewModel.affiliates.isNotEmpty) {
sections.add(
AffiliateBrandGridWidget(
affiliateBrands: _viewModel.affiliates,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.pointPartner),
onTap: (item) {
// Get.toNamed(affiliateDetailScreen, arguments: item.brandId);
},
),
);
}
break;
default:
break;
}
} }
return sections; return sections;
} }
Widget? _buildSection(MainSectionConfigModel section) {
switch (section.headerSectionType) {
case HeaderSectionType.banner:
if (_viewModel.banners.isEmpty) return null;
return BannerCarousel(
banners: _viewModel.banners,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.banner),
onTap: (item) => item.directionalScreen?.begin(),
);
case HeaderSectionType.topButton:
if (_viewModel.services.isEmpty) return null;
return MainServiceGrid(
services: _viewModel.services,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.topButton),
onTap: (item) => item.directionalScreen?.begin(),
);
case HeaderSectionType.campaign:
if (_viewModel.achievements.isEmpty) return null;
return AchievementCarousel(
items: _viewModel.achievements,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.campaign),
onTap: (item) => item.directionScreen?.begin(),
);
case HeaderSectionType.product:
if (_viewModel.products.isEmpty) return null;
final displayProducts = List<ProductModel>.from(_viewModel.products);
if (displayProducts.length.isOdd) {
displayProducts.removeLast();
}
if (displayProducts.isEmpty) return null;
return ProductGrid(
products: displayProducts,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.product),
onTap: (product) => Get.toNamed(voucherDetailScreen, arguments: product.id),
);
case HeaderSectionType.news:
if (_viewModel.news.isEmpty) return null;
return NewsCarouselWidget(
items: _viewModel.news,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.news),
onTap: (item) => Get.toNamed(campaignDetailScreen, arguments: {"id": item.pageId}),
);
case HeaderSectionType.myProduct:
if (_viewModel.myProducts.isEmpty) return null;
return MyProductCarouselWidget(
items: _viewModel.myProducts,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.myProduct),
onTap: (item) => Get.toNamed(voucherDetailScreen, arguments: {"customerProductId": item.id}),
);
case HeaderSectionType.flashSale:
final products = _viewModel.flashSaleData.value?.products ?? [];
if (products.isEmpty) return null;
return FlashSaleCarouselWidget(
products: products,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.flashSale),
onTap: (product) => Get.toNamed(voucherDetailScreen, arguments: product.id),
);
case HeaderSectionType.brand:
if (_viewModel.brands.isEmpty) return null;
return BrandGridWidget(
brands: _viewModel.brands,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.brand),
onTap: (_) {},
);
case HeaderSectionType.pointPartner:
if (_viewModel.affiliates.isEmpty) return null;
return AffiliateBrandGridWidget(
affiliateBrands: _viewModel.affiliates,
sectionConfig: _viewModel.getMainSectionConfigModel(HeaderSectionType.pointPartner),
onTap: (_) {},
);
default:
return null;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final paddingBottom = MediaQuery.of(context).padding.bottom + 20; final paddingBottom = MediaQuery.of(context).padding.bottom + 20;
...@@ -193,14 +152,14 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit { ...@@ -193,14 +152,14 @@ class _HomeScreenState extends State<HomeScreen> with PopupOnInit {
body: Stack( body: Stack(
children: [ children: [
NestedScrollView( NestedScrollView(
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
headerSliverBuilder: (_, _) => [_buildSliverHeader(heightHeader)], headerSliverBuilder: (_, _) => [_buildSliverHeader(heightHeader)],
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: _onRefresh, onRefresh: _onRefresh,
child: Obx(() { child: Obx(() {
return ListView( return ListView(
padding: EdgeInsets.only(bottom: paddingBottom), padding: EdgeInsets.only(bottom: paddingBottom),
physics: AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),
children: _buildSectionContent(), children: _buildSectionContent(),
); );
}), }),
......
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
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';
...@@ -29,9 +30,22 @@ class HomeTabViewModel extends RestfulApiViewModel { ...@@ -29,9 +30,22 @@ class HomeTabViewModel extends RestfulApiViewModel {
final Rxn<FlashSaleModel> flashSaleData = Rxn<FlashSaleModel>(); final Rxn<FlashSaleModel> flashSaleData = Rxn<FlashSaleModel>();
final Rxn<HoverDataModel> hoverData = Rxn<HoverDataModel>(); final Rxn<HoverDataModel> hoverData = Rxn<HoverDataModel>();
late final Map<HeaderSectionType, Future<void> Function(MainSectionConfigModel)> _sectionLoaders;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
_sectionLoaders = {
HeaderSectionType.topButton: _loadTopButtons,
HeaderSectionType.banner: _loadBanners,
HeaderSectionType.campaign: _loadCampaigns,
HeaderSectionType.product: _loadProducts,
HeaderSectionType.news: _loadNews,
HeaderSectionType.flashSale: _loadFlashSale,
HeaderSectionType.brand: _loadBrands,
HeaderSectionType.pointPartner: _loadAffiliateBrands,
HeaderSectionType.myProduct: _loadMyProducts,
};
getSectionLayoutHome(); getSectionLayoutHome();
loadDataPiPiHome(); loadDataPiPiHome();
} }
...@@ -43,20 +57,9 @@ class HomeTabViewModel extends RestfulApiViewModel { ...@@ -43,20 +57,9 @@ class HomeTabViewModel extends RestfulApiViewModel {
Future<void> getSectionLayoutHome({bool showLoading = true}) async { Future<void> getSectionLayoutHome({bool showLoading = true}) async {
await callApi<List<MainSectionConfigModel>>( await callApi<List<MainSectionConfigModel>>(
request: () => client.getSectionLayoutHome(), request: () => client.getSectionLayoutHome(),
onSuccess: (data, _) { onSuccess: (data, _) => _resolveSectionLayouts(data),
sectionLayouts.assignAll(data); onFailure: (message, response, error) async =>
}, _resolveSectionLayouts(await _loadSectionLayoutHomeFromCache()),
onFailure: (_, _, _) async {
sectionLayouts.assignAll(await _loadSectionLayoutHomeFromCache());
},
onComplete: () async {
if (sectionLayouts.isEmpty) {
sectionLayouts.assignAll(await _loadSectionLayoutHomeFromCache());
}
for (final section in sectionLayouts) {
await _processSection(section);
}
},
withLoading: showLoading, withLoading: showLoading,
); );
} }
...@@ -74,78 +77,97 @@ class HomeTabViewModel extends RestfulApiViewModel { ...@@ -74,78 +77,97 @@ class HomeTabViewModel extends RestfulApiViewModel {
Future<List<MainSectionConfigModel>> _loadSectionLayoutHomeFromCache() async { Future<List<MainSectionConfigModel>> _loadSectionLayoutHomeFromCache() async {
final jsonStr = await rootBundle.loadString('assets/data/main_layout_section_home.json'); final jsonStr = await rootBundle.loadString('assets/data/main_layout_section_home.json');
final List<dynamic> jsonList = json.decode(jsonStr); final List<dynamic> jsonList = json.decode(jsonStr);
return jsonList.map((e) => MainSectionConfigModel.fromJson(e)).toList() ?? []; return jsonList.map((e) => MainSectionConfigModel.fromJson(e)).toList();
} }
Future<void> _processSection(MainSectionConfigModel section) async { Future<void> _resolveSectionLayouts(List<MainSectionConfigModel> layouts) async {
final path = section.apiList ?? ""; final resolved = layouts.isEmpty ? await _loadSectionLayoutHomeFromCache() : layouts;
switch (section.headerSectionType) { sectionLayouts.assignAll(resolved);
case HeaderSectionType.topButton: await _processSections(resolved);
final res = await client.fetchList<MainServiceModel>( }
path,
(json) => MainServiceModel.fromJson(json as Map<String, dynamic>), Future<void> _processSections(List<MainSectionConfigModel> sections) async {
); final futures = <Future<void>>[];
services.assignAll(res.data ?? []); for (final section in sections) {
break; final loader = _sectionLoaders[section.headerSectionType];
case HeaderSectionType.banner: if (loader != null) {
final res = await client.fetchList<BannerModel>( futures.add(loader(section));
path, } else {
(json) => BannerModel.fromJson(json as Map<String, dynamic>), debugPrint('HomeTabViewModel: Unsupported section type ${section.headerSectionType}');
); }
banners.assignAll(res.data ?? []);
break;
case HeaderSectionType.campaign:
final res = await client.fetchList<AchievementModel>(
path,
(json) => AchievementModel.fromJson(json as Map<String, dynamic>),
);
achievements.assignAll(res.data ?? []);
break;
case HeaderSectionType.product:
final res = await client.fetchList<ProductModel>(
path,
(json) => ProductModel.fromJson(json as Map<String, dynamic>),
);
products.assignAll(res.data ?? []);
break;
case HeaderSectionType.news:
final res = await client.fetchList<PageItemModel>(
path,
(json) => PageItemModel.fromJson(json as Map<String, dynamic>),
);
news.assignAll(res.data ?? []);
break;
case HeaderSectionType.flashSale:
final res = await client.fetchObject<FlashSaleModel>(
path,
(json) => FlashSaleModel.fromJson(json as Map<String, dynamic>),
);
flashSaleData.value = res.data;
break;
case HeaderSectionType.brand:
final res = await client.fetchList<BrandModel>(
path,
(json) => BrandModel.fromJson(json as Map<String, dynamic>),
);
brands.assignAll(res.data ?? []);
break;
case HeaderSectionType.pointPartner:
final res = await client.fetchList<AffiliateBrandModel>(
path,
(json) => AffiliateBrandModel.fromJson(json as Map<String, dynamic>),
);
affiliates.assignAll((res.data ?? []).take(6).toList());
break;
case HeaderSectionType.myProduct:
final res = await client.fetchList<MyProductModel>(
path,
(json) => MyProductModel.fromJson(json as Map<String, dynamic>),
);
myProducts.assignAll(res.data ?? []);
break;
default:
print("Unknown section type: ${section.headerSectionType}");
break;
} }
await Future.wait(futures);
}
Future<void> _loadTopButtons(MainSectionConfigModel section) async {
final res = await client.fetchList<MainServiceModel>(
section.apiList ?? '',
(json) => MainServiceModel.fromJson(json as Map<String, dynamic>),
);
services.assignAll(res.data ?? []);
}
Future<void> _loadBanners(MainSectionConfigModel section) async {
final res = await client.fetchList<BannerModel>(
section.apiList ?? '',
(json) => BannerModel.fromJson(json as Map<String, dynamic>),
);
banners.assignAll(res.data ?? []);
}
Future<void> _loadCampaigns(MainSectionConfigModel section) async {
final res = await client.fetchList<AchievementModel>(
section.apiList ?? '',
(json) => AchievementModel.fromJson(json as Map<String, dynamic>),
);
achievements.assignAll(res.data ?? []);
}
Future<void> _loadProducts(MainSectionConfigModel section) async {
final res = await client.fetchList<ProductModel>(
section.apiList ?? '',
(json) => ProductModel.fromJson(json as Map<String, dynamic>),
);
products.assignAll(res.data ?? []);
}
Future<void> _loadNews(MainSectionConfigModel section) async {
final res = await client.fetchList<PageItemModel>(
section.apiList ?? '',
(json) => PageItemModel.fromJson(json as Map<String, dynamic>),
);
news.assignAll(res.data ?? []);
}
Future<void> _loadFlashSale(MainSectionConfigModel section) async {
final res = await client.fetchObject<FlashSaleModel>(
section.apiList ?? '',
(json) => FlashSaleModel.fromJson(json as Map<String, dynamic>),
);
flashSaleData.value = res.data;
}
Future<void> _loadBrands(MainSectionConfigModel section) async {
final res = await client.fetchList<BrandModel>(
section.apiList ?? '',
(json) => BrandModel.fromJson(json as Map<String, dynamic>),
);
brands.assignAll(res.data ?? []);
}
Future<void> _loadAffiliateBrands(MainSectionConfigModel section) async {
final res = await client.fetchList<AffiliateBrandModel>(
section.apiList ?? '',
(json) => AffiliateBrandModel.fromJson(json as Map<String, dynamic>),
);
affiliates.assignAll((res.data ?? []).take(6).toList());
}
Future<void> _loadMyProducts(MainSectionConfigModel section) async {
final res = await client.fetchList<MyProductModel>(
section.apiList ?? '',
(json) => MyProductModel.fromJson(json as Map<String, dynamic>),
);
myProducts.assignAll(res.data ?? []);
} }
} }
\ No newline at end of file
...@@ -30,13 +30,12 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba ...@@ -30,13 +30,12 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
@override @override
void initState() { void initState() {
super.initState(); super.initState();
ever(_viewModel.errorMessage, (value) { _viewModel.onShowAlertError = (message) {
if (value != null && value.toString().isNotEmpty) { if (message.isEmpty) return;
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showAlertError(content: value); showAlertError(content: message);
}); });
} };
});
DetailPageRuleType? type; DetailPageRuleType? type;
String? pageId; String? pageId;
final args = Get.arguments; final args = Get.arguments;
...@@ -52,7 +51,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba ...@@ -52,7 +51,7 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
return Scaffold( return Scaffold(
backgroundColor: BaseColor.second200, backgroundColor: BaseColor.second200,
body: Obx(() { body: Obx(() {
CampaignDetailModel? pageDetail = _viewModel.campaignDetail.value.data?.pageDetail; CampaignDetailModel? pageDetail = _viewModel.campaignDetail.value?.pageDetail;
if (pageDetail == null) { if (pageDetail == null) {
return Stack( return Stack(
children: [ children: [
......
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:mypoint_flutter_app/configs/constants.dart'; import 'package:mypoint_flutter_app/configs/constants.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 '../../base/base_response_model.dart';
import '../../networking/restful_api_viewmodel.dart'; import '../../networking/restful_api_viewmodel.dart';
import 'model/campaign_detail_model.dart'; import 'model/campaign_detail_model.dart';
import 'model/detail_page_rule_type.dart'; import 'model/detail_page_rule_type.dart';
import '../faqs/faqs_model.dart';
class CampaignDetailViewModel extends RestfulApiViewModel { class CampaignDetailViewModel extends RestfulApiViewModel {
var campaignDetail = BaseResponseModel<CampaignDetailResponseModel>().obs; var campaignDetail = Rxn<CampaignDetailResponseModel>();
var isLoading = false.obs; void Function(String message)? onShowAlertError;
var errorMessage = "".obs;
void fetchData(DetailPageRuleType? type, String? pageId) { Future<void> fetchData(DetailPageRuleType? type, String? pageId) async {
if ((pageId ?? "").isNotEmpty) { if ((pageId ?? "").isNotEmpty) {
fetchWebsitePageGetDetail(pageId!); await fetchWebsitePageGetDetail(pageId!);
return; return;
} }
if (type != null) { if (type != null) {
fetchWebsitePage(type!); await fetchWebsitePage(type);
return; return;
} }
fetchFAQItems(); await fetchFAQItems();
} }
Future<void> fetchFAQItems() async { Future<void> fetchFAQItems() async {
showLoading(); await callApi<FAQItemModelResponse>(
isLoading(true); request: () => client.websiteFolderGetPageList({"folder_uri": "ABOUT"}),
client.websiteFolderGetPageList({"folder_uri": "ABOUT"}).then((value) { onSuccess: (data, _) async {
hideLoading(); final pageId = (data.items ?? [])
isLoading(false); .firstWhereOrNull((item) => (item.pageId ?? "").isNotEmpty)
final pageId = (value.data?.items ?? []).first.pageId ?? ""; ?.pageId ??
if (pageId.isEmpty) { "";
errorMessage.value = Constants.commonError; if (pageId.isEmpty) {
} else { onShowAlertError?.call(Constants.commonError);
fetchWebsitePageGetDetail(pageId); return;
} }
}); await fetchWebsitePageGetDetail(pageId);
},
onFailure: (msg, _, _) async {
onShowAlertError?.call(msg);
},
);
} }
void fetchWebsitePage(DetailPageRuleType type) { Future<void> fetchWebsitePage(DetailPageRuleType type) async {
showLoading(); await callApi<CampaignDetailResponseModel>(
isLoading(true); request: () => client.websitePage(type),
client.websitePage(type).then((value) { onSuccess: (data, _) {
campaignDetail.value = value; campaignDetail.value = data;
if (!value.isSuccess) { },
errorMessage.value = value.errorMessage ?? Constants.commonError; onFailure: (msg, _, _) async {
} onShowAlertError?.call(msg);
hideLoading(); },
isLoading(false); );
});
} }
void fetchWebsitePageGetDetail(String pageId) { Future<void> fetchWebsitePageGetDetail(String pageId) async {
showLoading(); await callApi<CampaignDetailResponseModel>(
isLoading(true); request: () => client.websitePageGetDetail(pageId),
client.websitePageGetDetail(pageId).then((value) { onSuccess: (data, response) {
campaignDetail.value = value; campaignDetail.value = data;
if (!value.isSuccess) { },
errorMessage.value = value.errorMessage ?? Constants.commonError; onFailure: (msg, response, error) async {
} onShowAlertError?.call(msg);
hideLoading(); },
isLoading(false); );
});
} }
Future<void> submitShareContent() async { Future<void> submitShareContent() async {
final path = campaignDetail.value.data?.pageDetail?.clickButtonShare ?? ""; final path = campaignDetail.value?.pageDetail?.clickButtonShare ?? "";
if (path.isEmpty) return; if (path.isEmpty) return;
await client.submitShareContent(path); await client.submitShareContent(path);
} }
......
...@@ -4,6 +4,7 @@ import 'package:get/get.dart'; ...@@ -4,6 +4,7 @@ import 'package:get/get.dart';
import 'package:mypoint_flutter_app/directional/directional_screen.dart'; import 'package:mypoint_flutter_app/directional/directional_screen.dart';
import 'package:mypoint_flutter_app/extensions/num_extension.dart'; import 'package:mypoint_flutter_app/extensions/num_extension.dart';
import 'package:mypoint_flutter_app/preference/data_preference.dart'; import 'package:mypoint_flutter_app/preference/data_preference.dart';
import 'package:mypoint_flutter_app/preference/point/point_manager.dart';
import '../../base/base_screen.dart'; import '../../base/base_screen.dart';
import '../../base/basic_state.dart'; import '../../base/basic_state.dart';
import '../../directional/directional_action_type.dart'; import '../../directional/directional_action_type.dart';
...@@ -26,24 +27,17 @@ class PersonalScreen extends BaseScreen { ...@@ -26,24 +27,17 @@ class PersonalScreen extends BaseScreen {
class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, PopupOnInit { class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, PopupOnInit {
final _headerHomeVM = Get.find<HeaderHomeViewModel>(); final _headerHomeVM = Get.find<HeaderHomeViewModel>();
String? _version, _buildNumber; final _pointManager = UserPointManager();
late final Future<List<String?>> _appInfoFuture;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadAppInfo(); _appInfoFuture = Future.wait([AppInfoHelper.version, AppInfoHelper.buildNumber]);
_pointManager.fetchUserPoint();
runPopupCheck(DirectionalScreenName.personal); runPopupCheck(DirectionalScreenName.personal);
} }
Future<void> _loadAppInfo() async {
final v = await AppInfoHelper.version;
final b = await AppInfoHelper.buildNumber;
setState(() {
_version = v ?? "";
_buildNumber = b ?? "";
});
}
@override @override
Widget createBody() { Widget createBody() {
return Scaffold( return Scaffold(
...@@ -56,7 +50,17 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po ...@@ -56,7 +50,17 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
children: [ children: [
_buildInvitationSection(), _buildInvitationSection(),
_buildMenuItems(), _buildMenuItems(),
_buildVersionInfo(_version, _buildNumber), FutureBuilder<List<String?>> (
future: _appInfoFuture,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
final version = snapshot.data?[0] ?? '';
final buildNumber = snapshot.data?[1] ?? '';
return _buildVersionInfo(version, buildNumber);
},
),
Container(color: Colors.grey[200], height: MediaQuery.of(context).padding.bottom + 16), Container(color: Colors.grey[200], height: MediaQuery.of(context).padding.bottom + 16),
], ],
), ),
...@@ -145,10 +149,12 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po ...@@ -145,10 +149,12 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
}, },
child: Row( child: Row(
children: [ children: [
Text( Obx(() {
(data.totalPointActive ?? 0).money(CurrencyUnit.point), return Text(
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), (_pointManager.pointStream.value).money(CurrencyUnit.point),
), style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
);
}),
const SizedBox(width: 4), const SizedBox(width: 4),
const Icon(Icons.chevron_right, color: Colors.white, size: 22), const Icon(Icons.chevron_right, color: Colors.white, size: 22),
], ],
...@@ -346,7 +352,6 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po ...@@ -346,7 +352,6 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
return; return;
} }
print("Safe back to login screen with phone: $phone, displayName: $displayName");
if (phone.isNotEmpty) { if (phone.isNotEmpty) {
await DataPreference.instance.clearLoginToken(); await DataPreference.instance.clearLoginToken();
Get.offAllNamed(loginScreen, arguments: {"phone": phone, 'fullName': displayName}); Get.offAllNamed(loginScreen, arguments: {"phone": phone, 'fullName': displayName});
......
...@@ -18,6 +18,8 @@ class CheckUpdateResponseModel { ...@@ -18,6 +18,8 @@ class CheckUpdateResponseModel {
switch (updateMode?.toUpperCase() ?? "") { switch (updateMode?.toUpperCase() ?? "") {
case 'NOW': case 'NOW':
return UpdateStatus.force; return UpdateStatus.force;
case 'NONE':
return UpdateStatus.none;
default: default:
return UpdateStatus.suggest; return UpdateStatus.suggest;
} }
...@@ -34,4 +36,3 @@ class CheckUpdateResponseModel { ...@@ -34,4 +36,3 @@ class CheckUpdateResponseModel {
Map<String, dynamic> toJson() => _$CheckUpdateResponseModelToJson(this); Map<String, dynamic> toJson() => _$CheckUpdateResponseModelToJson(this);
} }
...@@ -2,6 +2,7 @@ import 'dart:io'; ...@@ -2,6 +2,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:mypoint_flutter_app/preference/data_preference.dart';
import 'package:mypoint_flutter_app/screen/splash/splash_screen_viewmodel.dart'; import 'package:mypoint_flutter_app/screen/splash/splash_screen_viewmodel.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart'; import 'package:mypoint_flutter_app/shared/router_gage.dart';
import 'package:mypoint_flutter_app/widgets/alert/custom_alert_dialog.dart'; import 'package:mypoint_flutter_app/widgets/alert/custom_alert_dialog.dart';
...@@ -33,7 +34,7 @@ class _SplashScreenState extends BaseState<SplashScreen> with BasicState { ...@@ -33,7 +34,7 @@ class _SplashScreenState extends BaseState<SplashScreen> with BasicState {
} }
var status = updateData?.status ?? UpdateStatus.none; var status = updateData?.status ?? UpdateStatus.none;
if (status == UpdateStatus.none) { if (status == UpdateStatus.none) {
Get.offAllNamed(onboardingScreen); _viewModel.directionWhenTokenInvalid();
} else { } else {
_showSuggestUpdateAlert(updateData); _showSuggestUpdateAlert(updateData);
} }
......
...@@ -50,7 +50,7 @@ class SplashScreenViewModel extends RestfulApiViewModel { ...@@ -50,7 +50,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
final token = await _getTokenFromSDK(); final token = await _getTokenFromSDK();
if (token.orEmpty.isEmpty) { if (token.orEmpty.isEmpty) {
_directionWhenTokenInvalid(); directionWhenTokenInvalid();
return; return;
} }
...@@ -59,7 +59,7 @@ class SplashScreenViewModel extends RestfulApiViewModel { ...@@ -59,7 +59,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
} on Object catch (e) { } on Object catch (e) {
debugPrint('❌ SplashScreen - makeDataFollowInitApp error: $e'); debugPrint('❌ SplashScreen - makeDataFollowInitApp error: $e');
DataPreference.instance.clearLoginToken(); DataPreference.instance.clearLoginToken();
_directionWhenTokenInvalid(); directionWhenTokenInvalid();
} }
} }
...@@ -96,25 +96,30 @@ class SplashScreenViewModel extends RestfulApiViewModel { ...@@ -96,25 +96,30 @@ class SplashScreenViewModel extends RestfulApiViewModel {
final profile = await _fetchUserProfile(); final profile = await _fetchUserProfile();
if (profile == null) { if (profile == null) {
DataPreference.instance.clearLoginToken(); DataPreference.instance.clearLoginToken();
_directionWhenTokenInvalid(); directionWhenTokenInvalid();
return; return;
} }
await _prepareInitialData(profile); await _prepareInitialData(profile);
_goToMain(); _goToMain();
} }
void _directionWhenTokenInvalid() { Future<void> directionWhenTokenInvalid() async {
if (Get.currentRoute == onboardingScreen) return; if (Get.currentRoute == onboardingScreen) return;
Get.offAllNamed(onboardingScreen); if (kIsWeb) {
// if (kIsWeb) { print('❌ No token found on web, closing app');
// print('❌ No token found on web, closing app'); final closeSuccess = await webCloseApp({
// webCloseApp({ 'message': 'No token found, cannot proceed',
// 'message': 'No token found, cannot proceed', 'timestamp': DateTime.now().millisecondsSinceEpoch,
// 'timestamp': DateTime.now().millisecondsSinceEpoch, });
// }); if (closeSuccess) return;
// } else { }
// Get.toNamed(onboardingScreen); final phone = DataPreference.instance.phoneNumberUsedForLoginScreen;
// } final displayName = DataPreference.instance.displayName;
if (phone.isEmpty) {
Get.offAllNamed(onboardingScreen);
} else {
Get.offAllNamed(loginScreen, arguments: {"phone": phone, 'fullName': displayName});
}
} }
Future<ProfileResponseModel?> _fetchUserProfile() async { Future<ProfileResponseModel?> _fetchUserProfile() async {
......
...@@ -39,7 +39,7 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi ...@@ -39,7 +39,7 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
@override @override
void initState() { void initState() {
super.initState(); super.initState();
UserPointManager().fetchUserPoint();
int? productId; int? productId;
int? customerProductId; int? customerProductId;
...@@ -482,9 +482,12 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi ...@@ -482,9 +482,12 @@ class _VoucherDetailScreenState extends BaseState<VoucherDetailScreen> with Basi
}); });
} }
_handleRedeemProduct() { Future<void> _handleRedeemProduct() async {
final point = await UserPointManager().fetchUserPoint(withLoading: true) ?? 0;
final amountToBePaid = _viewModel.product.value?.amountToBePaid ?? 0; final amountToBePaid = _viewModel.product.value?.amountToBePaid ?? 0;
if (UserPointManager().point < amountToBePaid) { print('amountToBePaid: $amountToBePaid');
print('UserPointManager().point: $point');
if (point < amountToBePaid) {
showAlertError(content: "Bạn không đủ điểm để đổi ưu đãi này"); showAlertError(content: "Bạn không đủ điểm để đổi ưu đãi này");
return; return;
} }
......
...@@ -99,7 +99,7 @@ class VoucherDetailViewModel extends RestfulApiViewModel { ...@@ -99,7 +99,7 @@ class VoucherDetailViewModel extends RestfulApiViewModel {
}); });
} }
redeemProduct() { void redeemProduct() {
showLoading(); showLoading();
final requestId = Uuid().v4(); final requestId = Uuid().v4();
client client
......
...@@ -63,7 +63,7 @@ class VoucherListItem extends StatelessWidget { ...@@ -63,7 +63,7 @@ class VoucherListItem extends StatelessWidget {
child: loadNetworkImage( child: loadNetworkImage(
url: bgImage, url: bgImage,
fit: BoxFit.cover, fit: BoxFit.cover,
placeholderAsset: 'assets/images/sample.png', placeholderAsset: 'assets/images/bg_default_169.png',
), ),
), ),
if (!product.inStock) if (!product.inStock)
......
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