From dd4e2cf08c465528c8bc1c892c3124ab977c37ca Mon Sep 17 00:00:00 2001 From: zhx Date: Sat, 16 May 2026 01:58:02 +0800 Subject: [PATCH] Add profile card display and inventory --- .../store/data_card/store_data_card_page.dart | 207 +++++++++ lib/modules/store/store_page.dart | 134 +++--- .../data_card/bags_data_card_page.dart | 399 ++++++++++++++++++ lib/modules/user/my_items/my_items_page.dart | 3 + .../user/profile/person_detail_page.dart | 113 ++++- .../props_bag_data_card_detail_dialog.dart | 162 +++++++ .../props/sc_data_card_resource_view.dart | 214 +++++++++- .../props_store_data_card_detail_dialog.dart | 247 +++++++++++ 8 files changed, 1382 insertions(+), 97 deletions(-) create mode 100644 lib/modules/store/data_card/store_data_card_page.dart create mode 100644 lib/modules/user/my_items/data_card/bags_data_card_page.dart create mode 100644 lib/ui_kit/widgets/bag/props_bag_data_card_detail_dialog.dart create mode 100644 lib/ui_kit/widgets/store/props_store_data_card_detail_dialog.dart diff --git a/lib/modules/store/data_card/store_data_card_page.dart b/lib/modules/store/data_card/store_data_card_page.dart new file mode 100644 index 0000000..cd26c3d --- /dev/null +++ b/lib/modules/store/data_card/store_data_card_page.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:yumi/app/constants/sc_global_config.dart'; +import 'package:yumi/app_localizations.dart'; +import 'package:yumi/shared/business_logic/models/res/store_list_res.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_currency_type.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart'; +import 'package:yumi/ui_kit/components/sc_compontent.dart'; +import 'package:yumi/ui_kit/components/sc_page_list.dart'; +import 'package:yumi/ui_kit/widgets/store/props_store_data_card_detail_dialog.dart'; +import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart'; + +class StoreDataCardPage extends SCPageList { + const StoreDataCardPage({super.key}); + + @override + StoreDataCardPageState createState() => StoreDataCardPageState(); +} + +class StoreDataCardPageState + extends SCPageListState { + double disCount = 1; + + @override + void initState() { + super.initState(); + enablePullUp = false; + isGridView = true; + gridViewCount = 3; + padding = EdgeInsets.symmetric(horizontal: 6.w); + backgroundColor = Colors.transparent; + gridDelegate = SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: gridViewCount, + mainAxisSpacing: 12.w, + crossAxisSpacing: 12.w, + childAspectRatio: 0.78, + ); + loadData(1); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + body: + items.isEmpty && isLoading + ? const SCStoreGridSkeleton() + : buildList(context), + ); + } + + @override + Widget buildItem(StoreDataCardListResBean item) { + final res = item; + final prices = res.res.propsPrices ?? const []; + final firstPrice = prices.isNotEmpty ? prices.first : null; + return GestureDetector( + child: Container( + decoration: BoxDecoration( + color: + res.isSelecte + ? const Color(0xff18F2B1).withValues(alpha: 0.1) + : SCGlobalConfig.businessLogicStrategy + .getStoreItemBackgroundColor(), + border: Border.all( + color: + res.isSelecte + ? SCGlobalConfig.businessLogicStrategy + .getStoreItemSelectedBorderColor() + : SCGlobalConfig.businessLogicStrategy + .getStoreItemUnselectedBorderColor(), + width: 1.w, + ), + borderRadius: BorderRadius.all(Radius.circular(8.w)), + ), + child: Column( + children: [ + SizedBox(height: 10.w), + ClipRRect( + borderRadius: BorderRadius.circular(6.w), + child: netImage( + url: res.res.propsResources?.cover ?? "", + width: 72.w, + height: 50.w, + fit: BoxFit.cover, + ), + ), + SizedBox(height: 8.w), + buildStoreBagItemTitle( + res.res.propsResources?.name ?? "", + textColor: Colors.white, + fontSize: 11.sp, + ), + SizedBox(height: 3.w), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 10.w), + Expanded( + child: Text.rich( + textAlign: TextAlign.center, + TextSpan( + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Image.asset( + SCGlobalConfig.businessLogicStrategy + .getStoreItemGoldIcon(), + width: 22.w, + ), + ), + const TextSpan(text: " "), + if (disCount < 1) + TextSpan( + text: "${firstPrice?.amount ?? 0}", + style: TextStyle( + fontSize: 12.sp, + color: + SCGlobalConfig.businessLogicStrategy + .getStoreItemPriceTextColor(), + fontWeight: FontWeight.w600, + decoration: TextDecoration.lineThrough, + ), + ), + if (disCount < 1) const TextSpan(text: " "), + TextSpan( + text: + "${((firstPrice?.amount ?? 0) * disCount).toInt()}/${firstPrice?.days ?? 0} ${SCAppLocalizations.of(context)!.day}", + style: TextStyle( + fontSize: 12.sp, + color: + SCGlobalConfig.businessLogicStrategy + .getStoreItemPriceTextColor(), + fontWeight: FontWeight.w600, + ), + ), + ], + ), + strutStyle: const StrutStyle( + height: 1.3, + fontWeight: FontWeight.w500, + forceStrutHeight: true, + ), + ), + ), + SizedBox(width: 10.w), + ], + ), + ], + ), + ), + onTap: () { + _selectItem(res); + _showDetail(res.res); + }, + ); + } + + @override + loadPage({ + required int page, + required Function(List) onSuccess, + Function? onErr, + }) async { + try { + final beans = []; + final storeList = await SCStoreRepositoryImp().storeList( + SCCurrencyType.GOLD.name, + SCPropsType.DATA_CARD.name, + ); + for (final value in storeList) { + beans.add(StoreDataCardListResBean(value, false)); + } + onSuccess(beans); + } catch (_) { + onErr?.call(); + } + } + + void _showDetail(StoreListRes res) { + SmartDialog.show( + tag: "showPropsDetail", + alignment: Alignment.bottomCenter, + animationType: SmartAnimationType.fade, + builder: (_) { + return PropsStoreDataCardDetailDialog(res, disCount); + }, + ); + } + + void _selectItem(StoreDataCardListResBean res) { + for (final value in items) { + value.isSelecte = false; + } + res.isSelecte = true; + setState(() {}); + } +} + +class StoreDataCardListResBean { + StoreDataCardListResBean(this.res, this.isSelecte); + + StoreListRes res; + bool isSelecte; +} diff --git a/lib/modules/store/store_page.dart b/lib/modules/store/store_page.dart index 42c09d3..9132312 100644 --- a/lib/modules/store/store_page.dart +++ b/lib/modules/store/store_page.dart @@ -1,22 +1,15 @@ import 'dart:async'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:yumi/ui_kit/components/sc_compontent.dart'; import 'package:yumi/ui_kit/components/text/sc_text.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/modules/store/store_route.dart'; -import 'package:yumi/modules/store/theme/store_theme_page.dart'; +import 'package:yumi/modules/store/data_card/store_data_card_page.dart'; import 'package:provider/provider.dart'; -import 'package:tancent_vap/utils/constant.dart'; -import 'package:tancent_vap/widgets/vap_view.dart'; import 'package:yumi/app_localizations.dart'; import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; -import 'package:yumi/shared/tools/sc_path_utils.dart'; -import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart'; import 'package:yumi/services/auth/user_profile_manager.dart'; -import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart'; import 'package:yumi/modules/wallet/wallet_route.dart'; import 'package:yumi/modules/store/chatbox/store_chatbox_page.dart'; import 'package:yumi/modules/store/headdress/store_headdress_page.dart'; @@ -25,8 +18,10 @@ import 'package:yumi/modules/store/mountains/store_mountains_page.dart'; import '../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart'; class StorePage extends StatefulWidget { + const StorePage({super.key}); + @override - _StorePageState createState() => _StorePageState(); + State createState() => _StorePageState(); } class _StorePageState extends State @@ -40,16 +35,11 @@ class _StorePageState extends State void initState() { super.initState(); _pages.add(StoreHeaddressPage()); - _pages.add( - StoreMountainsPage(), - ); - _pages.add( - StoreThemePage(), - ); + _pages.add(StoreMountainsPage()); + _pages.add(const StoreDataCardPage()); _pages.add(StoreChatboxPage()); _tabController = TabController(length: _pages.length, vsync: this); - _tabController.addListener(() { - }); + _tabController.addListener(() {}); Provider.of(context, listen: false).balance(); } @@ -64,7 +54,7 @@ class _StorePageState extends State _tabs.clear(); _tabs.add(Tab(text: SCAppLocalizations.of(context)!.headdress)); _tabs.add(Tab(text: SCAppLocalizations.of(context)!.mountains)); - _tabs.add(Tab(text: SCAppLocalizations.of(context)!.theme)); + _tabs.add(const Tab(text: 'Profile Card')); _tabs.add(Tab(text: SCAppLocalizations.of(context)!.chatBox)); return Stack( children: [ @@ -86,10 +76,10 @@ class _StorePageState extends State padding: EdgeInsets.all(5.w), margin: EdgeInsetsDirectional.only( end: - SCGlobalConfig.businessLogicStrategy - .getStorePageShoppingBagMargin() - .end - .w, + SCGlobalConfig.businessLogicStrategy + .getStorePageShoppingBagMargin() + .end + .w, ), child: Image.asset( SCGlobalConfig.businessLogicStrategy @@ -122,12 +112,12 @@ class _StorePageState extends State unselectedLabelStyle: SCGlobalConfig.businessLogicStrategy .getStorePageTabUnselectedLabelStyle() .copyWith( - fontSize: - SCGlobalConfig.businessLogicStrategy - .getStorePageTabUnselectedLabelStyle() - .fontSize - ?.sp, - ), + fontSize: + SCGlobalConfig.businessLogicStrategy + .getStorePageTabUnselectedLabelStyle() + .fontSize + ?.sp, + ), // indicatorPadding: EdgeInsets.symmetric( // vertical: 5.w, // horizontal: 15.w, @@ -135,12 +125,12 @@ class _StorePageState extends State indicator: SCFixedWidthTabIndicator( width: 15.w, color: - SCGlobalConfig.businessLogicStrategy - .getStorePageTabIndicatorColor(), + SCGlobalConfig.businessLogicStrategy + .getStorePageTabIndicatorColor(), ), dividerColor: - SCGlobalConfig.businessLogicStrategy - .getStorePageTabDividerColor(), + SCGlobalConfig.businessLogicStrategy + .getStorePageTabDividerColor(), controller: _tabController, tabs: _tabs, ), @@ -158,48 +148,48 @@ class _StorePageState extends State return GestureDetector( child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(12.w), - topRight: Radius.circular(12.w), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(12.w), + topRight: Radius.circular(12.w), + ), + color: Color(0xff18F2B1).withValues(alpha: 0.1), + ), + padding: EdgeInsets.only(bottom: 12.w, top: 12.w), + child: Row( + children: [ + SizedBox(width: 10.w), + Image.asset( + SCGlobalConfig.businessLogicStrategy + .getStorePageGoldIcon(), + width: 25.w, + height: 25.w, ), - color: Color(0xff18F2B1).withOpacity(0.1), + SizedBox(width: 5.w), + text( + "${ref.myBalance}", + fontSize: 14.sp, + textColor: + SCGlobalConfig.businessLogicStrategy + .getStorePageGoldTextColor(), + ), + SizedBox(width: 4.w), + Icon( + Icons.chevron_right, + size: 20.w, + color: + SCGlobalConfig.businessLogicStrategy + .getStorePageGoldIconColor(), + ), + ], + ), ), - padding: EdgeInsets.only(bottom: 12.w,top: 12.w), - child: Row( - children: [ - SizedBox(width: 10.w), - Image.asset( - SCGlobalConfig.businessLogicStrategy - .getStorePageGoldIcon(), - width: 25.w, - height: 25.w, - ), - SizedBox(width: 5.w), - text( - "${ref.myBalance}", - fontSize: 14.sp, - textColor: - SCGlobalConfig.businessLogicStrategy - .getStorePageGoldTextColor(), - ), - SizedBox(width: 4.w), - Icon( - Icons.chevron_right, - size: 20.w, - color: - SCGlobalConfig.businessLogicStrategy - .getStorePageGoldIconColor(), - ), - ], - ), - ), - onTap: () { - SCNavigatorUtils.push( - context, - WalletRoute.recharge, - replace: false, - ); - }, + onTap: () { + SCNavigatorUtils.push( + context, + WalletRoute.recharge, + replace: false, + ); + }, ); }, ), diff --git a/lib/modules/user/my_items/data_card/bags_data_card_page.dart b/lib/modules/user/my_items/data_card/bags_data_card_page.dart new file mode 100644 index 0000000..ca669b1 --- /dev/null +++ b/lib/modules/user/my_items/data_card/bags_data_card_page.dart @@ -0,0 +1,399 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:provider/provider.dart'; +import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/app_localizations.dart'; +import 'package:yumi/modules/store/store_route.dart'; +import 'package:yumi/services/auth/user_profile_manager.dart'; +import 'package:yumi/shared/business_logic/models/res/bags_list_res.dart'; +import 'package:yumi/shared/business_logic/models/res/login_res.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart'; +import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart'; +import 'package:yumi/shared/tools/sc_loading_manager.dart'; +import 'package:yumi/ui_kit/components/dialog/dialog_base.dart'; +import 'package:yumi/ui_kit/components/sc_compontent.dart'; +import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; +import 'package:yumi/ui_kit/components/sc_page_list.dart'; +import 'package:yumi/ui_kit/components/sc_tts.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; +import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; +import 'package:yumi/ui_kit/widgets/bag/props_bag_data_card_detail_dialog.dart'; +import 'package:yumi/ui_kit/widgets/countdown_timer.dart'; +import 'package:yumi/ui_kit/widgets/store/store_bag_page_helpers.dart'; + +class BagsDataCardPage extends SCPageList { + const BagsDataCardPage({super.key}); + + @override + BagsDataCardPageState createState() => BagsDataCardPageState(); +} + +class BagsDataCardPageState + extends SCPageListState { + PropsResources? myDataCard; + BagsListRes? selectedDataCard; + + @override + void initState() { + super.initState(); + enablePullUp = false; + isGridView = true; + gridViewCount = 3; + padding = EdgeInsets.symmetric(horizontal: 6.w); + backgroundColor = Colors.transparent; + gridDelegate = SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: gridViewCount, + mainAxisSpacing: 12.w, + crossAxisSpacing: 12.w, + childAspectRatio: 0.88, + ); + myDataCard = AccountStorage().getDataCard(); + loadData(1); + } + + @override + Widget build(BuildContext context) { + final hasSelection = selectedDataCard != null; + final selectedExpired = + (selectedDataCard?.expireTime ?? 0) < + DateTime.now().millisecondsSinceEpoch; + final selectedInUse = + myDataCard?.id == selectedDataCard?.propsResources?.id; + + return Scaffold( + backgroundColor: Colors.transparent, + body: Column( + children: [ + Expanded( + child: + items.isEmpty && isLoading + ? const SCBagGridSkeleton() + : buildList(context), + ), + hasSelection ? SizedBox(height: 10.w) : const SizedBox.shrink(), + hasSelection + ? Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(12.w), + topRight: Radius.circular(12.w), + ), + color: const Color(0xff18F2B1).withValues(alpha: 0.1), + ), + child: Column( + children: [ + SizedBox(height: 5.w), + Row( + spacing: 5.w, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + text( + "${SCAppLocalizations.of(context)!.expirationTime}:", + textColor: Colors.white, + fontSize: 12.sp, + ), + CountdownTimer( + fontSize: 11.sp, + color: SocialChatTheme.primaryLight, + expiryDate: DateTime.fromMillisecondsSinceEpoch( + selectedDataCard?.expireTime ?? 0, + ), + ), + ], + ), + SizedBox(height: 10.w), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SCDebounceWidget( + child: Container( + alignment: Alignment.center, + width: 120.w, + height: 35.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.w), + color: SocialChatTheme.primaryLight, + ), + child: text( + SCAppLocalizations.of(context)!.renewal, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textColor: Colors.white, + ), + ), + onTap: () { + SCNavigatorUtils.push( + context, + StoreRoute.list, + replace: false, + ); + }, + ), + SizedBox(width: 10.w), + SCDebounceWidget( + child: Container( + alignment: Alignment.center, + width: 120.w, + height: 35.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.w), + color: + selectedInUse + ? Colors.white + : SocialChatTheme.primaryLight, + ), + child: text( + selectedExpired + ? SCAppLocalizations.of(context)!.renewal + : selectedInUse + ? SCAppLocalizations.of(context)!.inUse + : SCAppLocalizations.of(context)!.use, + fontWeight: FontWeight.w600, + fontSize: 14.sp, + textColor: + selectedInUse + ? const Color(0xffB1B1B1) + : Colors.white, + ), + ), + onTap: () { + if (selectedExpired) { + SCNavigatorUtils.push( + context, + StoreRoute.list, + replace: false, + ); + return; + } + if (!selectedInUse) { + SmartDialog.show( + tag: "showConfirmDialog", + alignment: Alignment.center, + debounce: true, + animationType: SmartAnimationType.fade, + builder: (_) { + return MsgDialog( + title: SCAppLocalizations.of(context)!.tips, + msg: + SCAppLocalizations.of( + context, + )!.confirmUseTips, + btnText: + SCAppLocalizations.of(context)!.confirm, + onEnsure: () { + _use(selectedDataCard!, false); + }, + ); + }, + ); + return; + } + SmartDialog.show( + tag: "showConfirmDialog", + alignment: Alignment.center, + debounce: true, + animationType: SmartAnimationType.fade, + builder: (_) { + return MsgDialog( + title: SCAppLocalizations.of(context)!.tips, + msg: + SCAppLocalizations.of( + context, + )!.confirmUnUseTips, + btnText: + SCAppLocalizations.of(context)!.confirm, + onEnsure: () { + _use(selectedDataCard!, true); + }, + ); + }, + ); + }, + ), + ], + ), + SizedBox(height: 10.w), + ], + ), + ) + : const SizedBox.shrink(), + ], + ), + ); + } + + @override + Widget buildItem(BagsListRes item) { + final res = item; + final selected = + selectedDataCard?.propsResources?.id == res.propsResources?.id; + return GestureDetector( + child: Container( + decoration: BoxDecoration( + color: const Color(0xff18F2B1).withValues(alpha: 0.1), + border: Border.all( + color: selected ? SocialChatTheme.primaryLight : Colors.transparent, + width: 1.w, + ), + borderRadius: BorderRadius.all(Radius.circular(8.w)), + ), + child: Stack( + alignment: Alignment.center, + children: [ + Column( + children: [ + SizedBox(height: 25.w), + ClipRRect( + borderRadius: BorderRadius.circular(6.w), + child: netImage( + url: res.propsResources?.cover ?? "", + width: 72.w, + height: 50.w, + fit: BoxFit.cover, + ), + ), + SizedBox(height: 8.w), + buildStoreBagItemTitle( + res.propsResources?.name ?? "", + textColor: Colors.white, + fontSize: 12.sp, + ), + ], + ), + PositionedDirectional( + top: 0, + start: 0, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 5.w), + decoration: BoxDecoration( + color: SocialChatTheme.primaryLight, + borderRadius: BorderRadiusDirectional.only( + topStart: Radius.circular(6.w), + topEnd: Radius.circular(12.w), + bottomEnd: Radius.circular(12.w), + ), + ), + child: Row( + children: [ + CountdownTimer( + fontSize: 9.sp, + color: Colors.white, + expiryDate: DateTime.fromMillisecondsSinceEpoch( + res.expireTime ?? 0, + ), + ), + SizedBox(width: 3.w), + Image.asset( + "sc_images/store/sc_icon_bag_clock.png", + width: 22.w, + height: 22.w, + ), + ], + ), + ), + ), + PositionedDirectional( + bottom: 5.w, + end: 5.w, + child: GestureDetector( + onTap: () { + selectedDataCard = res; + setState(() {}); + _showDetail(res); + }, + behavior: HitTestBehavior.opaque, + child: Container( + padding: EdgeInsets.all(5.w), + child: Image.asset( + "sc_images/store/sc_icon_shop_item_search.png", + width: 18.w, + height: 18.w, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ), + onTap: () { + selectedDataCard = res; + setState(() {}); + }, + ); + } + + @override + loadPage({ + required int page, + required Function(List) onSuccess, + Function? onErr, + }) async { + try { + final storeList = await SCStoreRepositoryImp().storeBackpack( + AccountStorage().getCurrentUser()?.userProfile?.id ?? "", + SCPropsType.DATA_CARD.name, + ); + for (final item in storeList) { + if (item.propsResources?.id == myDataCard?.id) { + selectedDataCard = item; + continue; + } + } + onSuccess(storeList); + } catch (_) { + onErr?.call(); + } + } + + void _showDetail(BagsListRes res) { + SmartDialog.show( + tag: "showPropsDetail", + alignment: Alignment.center, + animationType: SmartAnimationType.fade, + builder: (_) { + return PropsBagDataCardDetailDialog(res); + }, + onDismiss: () { + myDataCard = AccountStorage().getDataCard(); + setState(() {}); + }, + ); + } + + void _use(BagsListRes res, bool unload) { + SCLoadingManager.show(context: context); + final profileManager = Provider.of( + context, + listen: false, + ); + final localizations = SCAppLocalizations.of(context)!; + SCStoreRepositoryImp() + .switchPropsUse( + SCPropsType.DATA_CARD.name, + res.propsResources?.id ?? "", + unload, + ) + .then((value) async { + if (!mounted) { + SCLoadingManager.hide(); + return; + } + setState(() {}); + if (!unload) { + myDataCard = res.propsResources; + SCTts.show(localizations.successfulWear); + } else { + myDataCard = null; + SCTts.show(localizations.successfullyUnloaded); + } + profileManager.fetchUserProfileData(loadGuardCount: false); + SCLoadingManager.hide(); + }) + .catchError((e) { + SCLoadingManager.hide(); + }); + } +} diff --git a/lib/modules/user/my_items/my_items_page.dart b/lib/modules/user/my_items/my_items_page.dart index 13abae2..2276720 100644 --- a/lib/modules/user/my_items/my_items_page.dart +++ b/lib/modules/user/my_items/my_items_page.dart @@ -6,6 +6,7 @@ import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/modules/store/store_route.dart'; import 'package:yumi/modules/user/my_items/chatbox/bags_chatbox_page.dart'; +import 'package:yumi/modules/user/my_items/data_card/bags_data_card_page.dart'; import 'package:yumi/modules/user/my_items/gift/bags_gift_page.dart'; import 'package:yumi/modules/user/my_items/headdress/bags_headdress_page.dart'; import 'package:yumi/modules/user/my_items/mountains/bags_mountains_page.dart'; @@ -33,6 +34,7 @@ class _MyItemsPageState extends State _pages.add(BagsMountainsPage()); _pages.add(BagsGiftPage()); _pages.add(BagsChatboxPage()); + _pages.add(const BagsDataCardPage()); _tabController = TabController(length: _pages.length, vsync: this); _tabController.addListener(() {}); } @@ -50,6 +52,7 @@ class _MyItemsPageState extends State _tabs.add(Tab(text: SCAppLocalizations.of(context)!.mountains)); _tabs.add(Tab(text: SCAppLocalizations.of(context)!.gift)); _tabs.add(Tab(text: SCAppLocalizations.of(context)!.chatBox)); + _tabs.add(const Tab(text: 'Profile Card')); return Stack( children: [ Image.asset( diff --git a/lib/modules/user/profile/person_detail_page.dart b/lib/modules/user/profile/person_detail_page.dart index 2539854..286bb38 100644 --- a/lib/modules/user/profile/person_detail_page.dart +++ b/lib/modules/user/profile/person_detail_page.dart @@ -24,6 +24,7 @@ import 'package:yumi/shared/tools/sc_loading_manager.dart'; import 'package:yumi/shared/tools/sc_pick_utils.dart'; import 'package:yumi/shared/business_logic/models/res/gift_res.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_gift_repository_imp.dart'; import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; @@ -31,6 +32,7 @@ import 'package:yumi/services/audio/rtm_manager.dart'; import 'package:yumi/services/auth/user_profile_manager.dart'; import 'package:yumi/services/general/sc_app_general_manager.dart'; import 'package:yumi/ui_kit/widgets/badge/sc_user_badge_strip.dart'; +import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart'; import '../../../app/constants/sc_screen.dart'; import '../../../shared/business_logic/models/res/sc_user_counter_res.dart'; @@ -578,9 +580,57 @@ class _PersonDetailPageState extends State { ); } + Widget _buildProfileDataCardBackground(PropsResources? dataCard) { + if (dataCard == null) { + return const ColoredBox(color: _profileBg); + } + return Stack( + fit: StackFit.expand, + children: [ + SCDataCardResourceView(resource: dataCard, fit: BoxFit.cover), + DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withValues(alpha: 0.26), + _profileBg.withValues(alpha: 0.54), + _profileBg.withValues(alpha: 0.78), + ], + stops: const [0, 0.45, 1], + ), + ), + ), + ], + ); + } + + PropsResources? _activeDataCard(SocialChatUserProfile? profile) { + final now = DateTime.now().millisecondsSinceEpoch; + for (final item in profile?.useProps ?? const []) { + final resource = item.propsResources; + if (resource?.type != SCPropsType.DATA_CARD.name) { + continue; + } + final expireText = item.expireTime?.trim() ?? ""; + final expireTime = int.tryParse(expireText); + if (expireText.isNotEmpty && (expireTime ?? 0) <= now) { + continue; + } + final source = resource?.sourceUrl?.trim() ?? ""; + final cover = resource?.cover?.trim() ?? ""; + if (source.isNotEmpty || cover.isNotEmpty) { + return resource; + } + } + return null; + } + Widget _buildProfileHeader( SocialChatUserProfileManager ref, List backgroundPhotos, + PropsResources? dataCard, ) { return SizedBox( height: 520.w, @@ -632,7 +682,7 @@ class _PersonDetailPageState extends State { left: 0, right: 0, bottom: 0, - child: Container(color: _profileBg), + child: _buildProfileDataCardBackground(dataCard), ), Positioned( top: 210.w, @@ -664,7 +714,7 @@ class _PersonDetailPageState extends State { Widget _buildProfileColumnContent() { return Container( width: double.infinity, - color: _profileBg, + color: Colors.transparent, padding: EdgeInsets.fromLTRB(10.w, 4.w, 10.w, 220.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -881,7 +931,7 @@ class _PersonDetailPageState extends State { left: 0, right: 0, bottom: 0, - child: Container(height: backgroundHeight, color: _profileBg), + child: SizedBox(height: backgroundHeight), ), if (hasRoom) Positioned( @@ -1127,6 +1177,8 @@ class _PersonDetailPageState extends State { : (ref.userProfile?.backgroundPhotos ?? [])) .where((t) => t.status == 1) .toList(); + final dataCard = + isProfileLoading ? null : _activeDataCard(ref.userProfile); return Directionality( textDirection: window.locale.languageCode == "ar" @@ -1137,27 +1189,38 @@ class _PersonDetailPageState extends State { child: Stack( children: [ Positioned.fill( - child: Column( + child: Stack( children: [ - SizedBox( - height: 300.w, - width: ScreenUtil().screenWidth, - child: - backgroundPhotos.isNotEmpty - ? netImage( - url: backgroundPhotos.first.url ?? "", - width: ScreenUtil().screenWidth, - height: 300.w, - fit: BoxFit.cover, - ) - : Image.asset( - 'sc_images/person/sc_icon_profile_card_default_bg.png', - width: ScreenUtil().screenWidth, - height: 300.w, - fit: BoxFit.cover, - ), + Column( + children: [ + SizedBox( + height: 300.w, + width: ScreenUtil().screenWidth, + child: + backgroundPhotos.isNotEmpty + ? netImage( + url: backgroundPhotos.first.url ?? "", + width: ScreenUtil().screenWidth, + height: 300.w, + fit: BoxFit.cover, + ) + : Image.asset( + 'sc_images/person/sc_icon_profile_card_default_bg.png', + width: ScreenUtil().screenWidth, + height: 300.w, + fit: BoxFit.cover, + ), + ), + const Expanded(child: ColoredBox(color: _profileBg)), + ], + ), + Positioned( + top: 250.w, + left: 0, + right: 0, + bottom: 0, + child: _buildProfileDataCardBackground(dataCard), ), - Expanded(child: Container(color: _profileBg)), ], ), ), @@ -1496,7 +1559,11 @@ class _PersonDetailPageState extends State { physics: const BouncingScrollPhysics(), child: Column( children: [ - _buildProfileHeader(ref, backgroundPhotos), + _buildProfileHeader( + ref, + backgroundPhotos, + dataCard, + ), _buildProfileColumnContent(), ], ), diff --git a/lib/ui_kit/widgets/bag/props_bag_data_card_detail_dialog.dart b/lib/ui_kit/widgets/bag/props_bag_data_card_detail_dialog.dart new file mode 100644 index 0000000..37122dd --- /dev/null +++ b/lib/ui_kit/widgets/bag/props_bag_data_card_detail_dialog.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_debouncer/flutter_debouncer.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:provider/provider.dart'; +import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/app_localizations.dart'; +import 'package:yumi/modules/store/store_route.dart'; +import 'package:yumi/services/auth/user_profile_manager.dart'; +import 'package:yumi/shared/business_logic/models/res/bags_list_res.dart'; +import 'package:yumi/shared/business_logic/models/res/login_res.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart'; +import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart'; +import 'package:yumi/shared/tools/sc_loading_manager.dart'; +import 'package:yumi/ui_kit/components/sc_tts.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; +import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; +import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart'; + +class PropsBagDataCardDetailDialog extends StatefulWidget { + const PropsBagDataCardDetailDialog(this.res, {super.key}); + + final BagsListRes res; + + @override + State createState() => + _PropsBagDataCardDetailDialogState(); +} + +class _PropsBagDataCardDetailDialogState + extends State { + final debouncer = Debouncer(); + PropsResources? myDataCard; + + @override + void initState() { + super.initState(); + myDataCard = AccountStorage().getDataCard(); + } + + @override + Widget build(BuildContext context) { + final expired = + (widget.res.expireTime ?? 0) < DateTime.now().millisecondsSinceEpoch; + final inUse = myDataCard?.id == widget.res.propsResources?.id; + + return Container( + width: 260.w, + height: 330.w, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10.w)), + ), + child: Stack( + children: [ + Column( + children: [ + SizedBox(height: 20.w), + text( + 'Profile Card', + fontSize: 16.sp, + textColor: Colors.black, + fontWeight: FontWeight.bold, + ), + SizedBox(height: 20.w), + ClipRRect( + borderRadius: BorderRadius.circular(10.w), + child: SizedBox( + width: 214.w, + height: 150.w, + child: SCDataCardResourceView( + resource: widget.res.propsResources, + fit: BoxFit.cover, + ), + ), + ), + SizedBox(height: 25.w), + GestureDetector( + child: Container( + alignment: Alignment.center, + width: 130.w, + padding: EdgeInsets.symmetric(vertical: 10.w), + decoration: BoxDecoration( + color: SocialChatTheme.primaryLight, + borderRadius: BorderRadius.circular(12.w), + ), + child: text( + expired + ? SCAppLocalizations.of(context)!.renewal + : inUse + ? SCAppLocalizations.of(context)!.unUse + : SCAppLocalizations.of(context)!.use, + fontSize: 16.sp, + textColor: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + onTap: () { + debouncer.debounce( + duration: const Duration(milliseconds: 350), + onDebounce: () { + if (expired) { + SCNavigatorUtils.push( + context, + StoreRoute.list, + replace: false, + ); + return; + } + _use(widget.res, inUse); + }, + ); + }, + ), + ], + ), + Positioned( + right: 20.w, + top: 18.w, + child: GestureDetector( + child: Icon(Icons.close, size: 20.w), + onTap: () { + SmartDialog.dismiss(tag: "showPropsDetail"); + }, + ), + ), + ], + ), + ); + } + + void _use(BagsListRes res, bool unload) { + SCLoadingManager.show(); + final profileManager = Provider.of( + context, + listen: false, + ); + final localizations = SCAppLocalizations.of(context)!; + SCStoreRepositoryImp() + .switchPropsUse( + SCPropsType.DATA_CARD.name, + res.propsResources?.id ?? "", + unload, + ) + .then((value) async { + await profileManager.fetchUserProfileData(loadGuardCount: false); + Future.delayed(const Duration(milliseconds: 400), () { + SCLoadingManager.hide(); + SmartDialog.dismiss(tag: "showPropsDetail"); + }); + if (!unload) { + SCTts.show(localizations.successfulWear); + } else { + SCTts.show(localizations.successfullyUnloaded); + } + }) + .catchError((e) { + SCLoadingManager.hide(); + }); + } +} diff --git a/lib/ui_kit/widgets/props/sc_data_card_resource_view.dart b/lib/ui_kit/widgets/props/sc_data_card_resource_view.dart index fb95204..357619f 100644 --- a/lib/ui_kit/widgets/props/sc_data_card_resource_view.dart +++ b/lib/ui_kit/widgets/props/sc_data_card_resource_view.dart @@ -1,6 +1,13 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:tancent_vap/utils/constant.dart'; +import 'package:tancent_vap/widgets/vap_view.dart'; import 'package:video_player/video_player.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; +import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart'; +import 'package:yumi/shared/tools/sc_path_utils.dart'; import 'package:yumi/ui_kit/components/sc_compontent.dart'; import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_pag_effect_overlay.dart'; import 'package:yumi/ui_kit/widgets/svga/sc_network_svga_widget.dart'; @@ -28,7 +35,11 @@ class SCDataCardResourceView extends StatelessWidget { final Widget? fallback; static bool isSupported(String? url) { - return _isSvga(url) || _isPag(url) || _isVideo(url) || _isImage(url); + return _isSvga(url) || + _isPag(url) || + _isVapVideo(url) || + _isVideo(url) || + _isImage(url); } @override @@ -73,6 +84,19 @@ class SCDataCardResourceView extends StatelessWidget { defaultBuilder: (_) => fallbackView, ); } + if (_isVapVideo(resourceUrl)) { + if (defaultTargetPlatform != TargetPlatform.android) { + return fallbackView; + } + return _LoopingDataCardVapVideo( + url: resourceUrl, + width: resolvedWidth, + height: resolvedHeight, + fit: fit, + loop: loop, + fallback: fallbackView, + ); + } if (_isVideo(resourceUrl)) { return _LoopingDataCardVideo( url: resourceUrl, @@ -152,8 +176,11 @@ class SCDataCardResourceView extends StatelessWidget { static bool _isPag(String? url) => _hasExtension(url, const ['.pag']); + static bool _isVapVideo(String? url) => + _hasExtension(url, const ['.mp4', '.vap']); + static bool _isVideo(String? url) => - _hasExtension(url, const ['.mp4', '.mov', '.m4v']); + _hasExtension(url, const ['.mov', '.m4v']); static bool _isImage(String? url) => _hasExtension(url, const [ '.png', @@ -278,3 +305,186 @@ class _LoopingDataCardVideoState extends State<_LoopingDataCardVideo> { ); } } + +class _LoopingDataCardVapVideo extends StatefulWidget { + const _LoopingDataCardVapVideo({ + required this.url, + this.width, + this.height, + required this.fit, + required this.loop, + required this.fallback, + }); + + final String url; + final double? width; + final double? height; + final BoxFit fit; + final bool loop; + final Widget fallback; + + @override + State<_LoopingDataCardVapVideo> createState() => + _LoopingDataCardVapVideoState(); +} + +class _LoopingDataCardVapVideoState extends State<_LoopingDataCardVapVideo> { + VapController? _controller; + int _playToken = 0; + bool _failed = false; + + @override + void didUpdateWidget(covariant _LoopingDataCardVapVideo oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.url != widget.url || + oldWidget.loop != widget.loop || + oldWidget.fit != widget.fit) { + _playToken++; + _stop(); + if (_failed) { + setState(() => _failed = false); + } + _play(); + } + } + + @override + void dispose() { + _playToken++; + _stop(); + _controller = null; + super.dispose(); + } + + void _handleViewCreated(VapController controller) { + _controller = controller; + controller.setAnimListener( + onFailed: (_, __, ___) => _markFailed(), + onVideoStart: () { + if (mounted && _failed) { + setState(() => _failed = false); + } + }, + ); + _play(); + } + + Future _play() async { + final controller = _controller; + final url = widget.url.trim(); + if (controller == null || url.isEmpty) { + return; + } + final token = ++_playToken; + try { + final playablePath = await _resolvePlayablePath(url); + if (!_isCurrent(token, controller)) { + return; + } + await controller.stop(); + if (!_isCurrent(token, controller)) { + return; + } + await controller.setMute(true); + await controller.setLoop(widget.loop ? -1 : 0); + await controller.setScaleType(_scaleTypeForFit(widget.fit).key); + if (!_isCurrent(token, controller)) { + return; + } + final pathType = SCPathUtils.getPathType(playablePath); + if (pathType == PathType.asset) { + unawaited( + controller.playAsset(playablePath).catchError((_) { + _markFailed(); + }), + ); + } else { + unawaited( + controller.playFile(_normalizeFilePath(playablePath)).catchError((_) { + _markFailed(); + }), + ); + } + } catch (_) { + if (_isCurrent(token, controller)) { + _markFailed(); + } + } + } + + bool _isCurrent(int token, VapController controller) { + return mounted && token == _playToken && identical(_controller, controller); + } + + Future _resolvePlayablePath(String url) async { + switch (SCPathUtils.getPathType(url)) { + case PathType.network: + final file = await FileCacheManager.getInstance().getFile(url: url); + return file.path; + case PathType.asset: + case PathType.file: + case PathType.unknown: + return url; + } + } + + String _normalizeFilePath(String path) { + final uri = Uri.tryParse(path); + if (uri != null && uri.scheme == 'file') { + return uri.toFilePath(); + } + return path; + } + + void _stop() { + final controller = _controller; + if (controller != null) { + unawaited(controller.stop().catchError((_) {})); + } + } + + void _markFailed() { + if (!mounted || _failed) { + return; + } + setState(() => _failed = true); + } + + ScaleType _scaleTypeForFit(BoxFit fit) { + switch (fit) { + case BoxFit.cover: + case BoxFit.fitWidth: + case BoxFit.fitHeight: + case BoxFit.none: + return ScaleType.centerCrop; + case BoxFit.fill: + return ScaleType.fitXY; + case BoxFit.contain: + case BoxFit.scaleDown: + return ScaleType.fitCenter; + } + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: widget.width, + height: widget.height, + child: ClipRect( + child: Stack( + fit: StackFit.expand, + children: [ + widget.fallback, + if (!_failed) + VapView( + scaleType: _scaleTypeForFit(widget.fit), + repeat: widget.loop ? -1 : 0, + mute: true, + onViewCreated: _handleViewCreated, + ), + ], + ), + ), + ); + } +} diff --git a/lib/ui_kit/widgets/store/props_store_data_card_detail_dialog.dart b/lib/ui_kit/widgets/store/props_store_data_card_detail_dialog.dart new file mode 100644 index 0000000..913866f --- /dev/null +++ b/lib/ui_kit/widgets/store/props_store_data_card_detail_dialog.dart @@ -0,0 +1,247 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:provider/provider.dart'; +import 'package:yumi/app/constants/sc_global_config.dart'; +import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/app_localizations.dart'; +import 'package:yumi/main.dart'; +import 'package:yumi/modules/wallet/wallet_route.dart'; +import 'package:yumi/services/auth/user_profile_manager.dart'; +import 'package:yumi/shared/business_logic/models/res/store_list_res.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_currency_type.dart'; +import 'package:yumi/shared/data_sources/models/enum/sc_props_type.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_store_repository_imp.dart'; +import 'package:yumi/shared/tools/sc_loading_manager.dart'; +import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; +import 'package:yumi/ui_kit/components/sc_tts.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; +import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; +import 'package:yumi/ui_kit/widgets/props/sc_data_card_resource_view.dart'; + +class PropsStoreDataCardDetailDialog extends StatefulWidget { + const PropsStoreDataCardDetailDialog(this.res, this.disCount, {super.key}); + + final StoreListRes res; + final double disCount; + + @override + State createState() => + _PropsStoreDataCardDetailDialogState(); +} + +class _PropsStoreDataCardDetailDialogState + extends State { + PropsPrices? currentPropsPrice; + int currentIndex = 0; + + @override + void initState() { + super.initState(); + currentPropsPrice = + (widget.res.propsPrices?.isNotEmpty ?? false) + ? widget.res.propsPrices![currentIndex] + : null; + } + + @override + Widget build(BuildContext context) { + return SafeArea( + top: false, + child: Container( + height: 360.w, + width: ScreenUtil().screenWidth, + decoration: BoxDecoration( + color: const Color(0xff03523a), + borderRadius: BorderRadius.all(Radius.circular(10.w)), + ), + child: Column( + children: [ + SizedBox(height: 18.w), + ClipRRect( + borderRadius: BorderRadius.circular(12.w), + child: SizedBox( + width: 220.w, + height: 145.w, + child: SCDataCardResourceView( + resource: widget.res.propsResources, + fit: BoxFit.cover, + ), + ), + ), + SizedBox(height: 18.w), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 15.w), + Expanded( + child: text( + widget.res.propsResources?.name ?? "", + textColor: Colors.white, + fontSize: 12.sp, + maxLines: 1, + ), + ), + Image.asset( + "sc_images/general/sc_icon_jb.png", + width: 22.w, + height: 22.w, + ), + if (widget.disCount < 1) + text( + "${currentPropsPrice?.amount ?? 0}", + textColor: SocialChatTheme.primaryLight, + fontWeight: FontWeight.w600, + decoration: TextDecoration.lineThrough, + ), + if (widget.disCount < 1) SizedBox(width: 5.w), + text( + "${((currentPropsPrice?.amount ?? 0) * widget.disCount).toInt()}", + textColor: SocialChatTheme.primaryLight, + fontSize: 13.sp, + fontWeight: FontWeight.w600, + ), + SizedBox(width: 15.w), + ], + ), + SizedBox(height: 10.w), + SizedBox( + height: 28.w, + child: ListView.separated( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + itemCount: widget.res.propsPrices?.length ?? 0, + itemBuilder: (context, index) { + return GestureDetector( + child: Container( + width: 110.w, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5.w), + color: const Color(0xff18F2B1).withValues(alpha: 0.1), + border: Border.all( + color: + index == currentIndex + ? SocialChatTheme.primaryLight + : Colors.transparent, + width: 1.w, + ), + ), + child: text( + "${widget.res.propsPrices?[index].days} ${SCAppLocalizations.of(context)!.day}", + textColor: SocialChatTheme.primaryLight, + ), + ), + onTap: () { + currentIndex = index; + currentPropsPrice = widget.res.propsPrices?[index]; + setState(() {}); + }, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return SizedBox(width: 5.w); + }, + ), + ), + SizedBox(height: 18.w), + Container( + color: Colors.black12, + width: ScreenUtil().screenWidth, + height: 0.5.w, + ), + SizedBox(height: 18.w), + Row( + children: [ + Consumer( + builder: (context, ref, child) { + return GestureDetector( + child: Row( + children: [ + SizedBox(width: 10.w), + Image.asset( + SCGlobalConfig.businessLogicStrategy + .getStorePageGoldIcon(), + width: 20.w, + height: 20.w, + ), + SizedBox(width: 5.w), + text( + "${ref.myBalance}", + fontSize: 13.sp, + textColor: SocialChatTheme.primaryLight, + ), + Icon( + Icons.chevron_right, + size: 20.w, + color: SocialChatTheme.primaryLight, + ), + ], + ), + onTap: () { + SmartDialog.dismiss(tag: "showPropsDetail"); + SCNavigatorUtils.push( + navigatorKey.currentState!.context, + WalletRoute.recharge, + replace: false, + ); + }, + ); + }, + ), + const Spacer(), + SCDebounceWidget( + onTap: _purchase, + child: Container( + alignment: Alignment.center, + width: 130.w, + padding: EdgeInsets.symmetric(vertical: 10.w), + decoration: BoxDecoration( + color: SocialChatTheme.primaryLight, + borderRadius: BorderRadius.circular(12.w), + ), + child: text( + SCAppLocalizations.of(context)!.purchase, + fontSize: 16.sp, + textColor: Colors.white, + fontWeight: FontWeight.w600, + ), + ), + ), + SizedBox(width: 10.w), + ], + ), + ], + ), + ), + ); + } + + void _purchase() { + if (currentPropsPrice == null) { + return; + } + final profileManager = Provider.of( + context, + listen: false, + ); + final localizations = SCAppLocalizations.of(context)!; + SCLoadingManager.show(); + SCStoreRepositoryImp() + .storePurchasing( + widget.res.id ?? "", + SCPropsType.DATA_CARD.name, + SCCurrencyType.GOLD.name, + "${currentPropsPrice?.days}", + ) + .then((value) { + SmartDialog.dismiss(tag: "showPropsDetail"); + SCLoadingManager.hide(); + SCTts.show(localizations.purchaseIsSuccessful); + profileManager.updateBalance(value); + }) + .catchError((e) { + SCLoadingManager.hide(); + }); + } +}