From e41cb3fb243bd1b8e8ec38983bfb069e4810546e Mon Sep 17 00:00:00 2001 From: roxy Date: Thu, 7 May 2026 14:25:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E8=B5=84=E6=96=99=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/index/main_route.dart | 10 + .../user/profile/person_detail_page.dart | 995 ++++++++++-------- .../profile_gift_wall_detail_page.dart | 495 +++++++++ .../business_logic/models/res/gift_res.dart | 432 ++++---- .../person/sc_icon_profile_following.png | Bin 0 -> 5316 bytes .../sc_icon_profile_gift_wall_title.png | Bin 0 -> 9214 bytes .../sc_icon_profile_wall_of_honors_title.png | Bin 0 -> 13881 bytes 需求进度.md | 9 +- 8 files changed, 1296 insertions(+), 645 deletions(-) create mode 100644 lib/modules/user/profile/profile_gift_wall_detail_page.dart create mode 100644 sc_images/person/sc_icon_profile_following.png create mode 100644 sc_images/person/sc_icon_profile_gift_wall_title.png create mode 100644 sc_images/person/sc_icon_profile_wall_of_honors_title.png diff --git a/lib/modules/index/main_route.dart b/lib/modules/index/main_route.dart index 20b281a..afe4552 100644 --- a/lib/modules/index/main_route.dart +++ b/lib/modules/index/main_route.dart @@ -13,6 +13,7 @@ import 'package:yumi/modules/admin/editing/sc_editing_user_room_page.dart'; import 'package:yumi/modules/admin/search/sc_edit_room_search_admin_page.dart'; import 'package:yumi/modules/admin/search/sc_edit_user_search_admin_page.dart'; import 'package:yumi/modules/user/profile/person_detail_page.dart'; +import 'package:yumi/modules/user/profile/profile_gift_wall_detail_page.dart'; import 'package:yumi/modules/search/sc_search_page.dart'; import 'package:yumi/modules/media/image_preview_page.dart'; import 'package:yumi/modules/media/video_player_page.dart'; @@ -26,6 +27,7 @@ import '../user/edit/edit_user_info_page2.dart'; class SCMainRoute implements SCIRouterProvider { static String report = '/main/report'; static String person = '/main/person'; + static String giftWall = '/main/person/giftWall'; static String edit = '/main/person/edit'; static String follow = '/main/me/follow'; static String vistors = '/main/me/vistors'; @@ -63,6 +65,14 @@ class SCMainRoute implements SCIRouterProvider { ), ), ); + router.define( + giftWall, + handler: Handler( + handlerFunc: + (_, params) => + ProfileGiftWallDetailPage(tageId: params['tageId']!.first), + ), + ); router.define( mainSearch, handler: Handler(handlerFunc: (_, params) => SearchPage()), diff --git a/lib/modules/user/profile/person_detail_page.dart b/lib/modules/user/profile/person_detail_page.dart index d292b35..5cb8481 100644 --- a/lib/modules/user/profile/person_detail_page.dart +++ b/lib/modules/user/profile/person_detail_page.dart @@ -4,10 +4,7 @@ import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter/services.dart'; -import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:yumi/modules/user/profile/profile/sc_profile_page.dart'; -import 'package:yumi/modules/user/profile/props/sc_giftwall_page.dart'; import 'package:yumi/ui_kit/components/sc_debounce_widget.dart'; import 'package:yumi/ui_kit/components/text/sc_text.dart'; import 'package:yumi/shared/tools/sc_room_utils.dart'; @@ -24,18 +21,18 @@ import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:yumi/app/routes/sc_routes.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/shared/tools/sc_loading_manager.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/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'; 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/theme/socialchat_theme.dart'; import '../../../app/constants/sc_screen.dart'; import '../../../shared/business_logic/models/res/sc_user_counter_res.dart'; import '../../../shared/business_logic/models/res/sc_user_identity_res.dart'; -import '../../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart'; import '../../../ui_kit/widgets/id/sc_special_id_badge.dart'; import '../../chat/chat_route.dart'; @@ -50,11 +47,11 @@ class PersonDetailPage extends StatefulWidget { _PersonDetailPageState createState() => _PersonDetailPageState(); } -class _PersonDetailPageState extends State - with SingleTickerProviderStateMixin { - late TabController _tabController; - final List _pages = []; - final List _tabs = []; +class _PersonDetailPageState extends State { + static const Color _profileBg = Color(0xff072121); + static const Color _cardBg = Color(0xff08251E); + static const Color _profileBorder = Color(0xffB2FBCC); + SCUserIdentityRes? userIdentity; bool isFollow = false; @@ -63,6 +60,7 @@ class _PersonDetailPageState extends State bool isBlacklist = false; Map counterMap = {}; + List giftWallList = []; // 添加滚动控制器 final ScrollController _scrollController = ScrollController(); @@ -72,19 +70,6 @@ class _PersonDetailPageState extends State void initState() { super.initState(); - // _pages.add(PersonDynamicListPage(false, widget.tageId)); - _pages.add(SCProfilePage(false, widget.tageId)); - _pages.add(SCGiftwallPage(widget.tageId)); - // _pages.add(RelationShipPage(false, widget.tageId)); - _tabController = TabController( - initialIndex: 0, - length: _pages.length, - vsync: this, - ); - _tabController.addListener(() { - setState(() {}); // 刷新UI - }); // 监听切换 - // 监听滚动 _scrollController.addListener(() { if (_scrollController.hasClients) { @@ -145,6 +130,7 @@ class _PersonDetailPageState extends State isBlacklistLoading = false; } userCounter(widget.tageId); + _loadGiftWall(); } void userCounter(String userId) async { @@ -158,13 +144,19 @@ class _PersonDetailPageState extends State setState(() {}); } - Widget _buildTab(int index, String text) { - return Tab(text: text); + void _loadGiftWall() { + SCGiftRepositoryImp() + .giftWall(widget.tageId) + .then((result) { + if (!mounted) return; + giftWallList = result; + setState(() {}); + }) + .catchError((e) {}); } @override void dispose() { - _tabController.dispose(); _scrollController.dispose(); super.dispose(); } @@ -399,12 +391,165 @@ class _PersonDetailPageState extends State ); } + Widget _buildHeaderBadgePlaceholder() { + return SizedBox( + height: 24.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [], + ), + ); + } + + Widget _buildHeaderIntroduction(SocialChatUserProfileManager ref) { + final intro = (ref.userProfile?.autograph ?? "").trim(); + return Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: Text.rich( + TextSpan( + children: [ + const TextSpan(text: "Introduction: "), + TextSpan(text: intro.isEmpty ? "--" : intro), + ], + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.start, + style: TextStyle( + color: Colors.white, + fontSize: 17.sp, + fontWeight: FontWeight.w400, + height: 1.28, + ), + ), + ); + } + + Widget _buildHeaderStats() { + return _buildGradientBorder( + width: 355.w, + height: 51.w, + radius: 8.w, + backgroundColor: _cardBg, + endAlpha: 0.42, + child: Row( + children: [ + _buildHeaderStatItem( + value: "${counterMap["INTERVIEW"]?.quantity ?? 0}", + label: SCAppLocalizations.of(context)!.vistors, + onTap: () { + if (widget.isMe == "true") { + SCNavigatorUtils.push(context, SCMainRoute.vistors); + } + }, + ), + _buildHeaderStatsDivider(), + _buildHeaderStatItem( + value: "${counterMap["SUBSCRIPTION"]?.quantity ?? 0}", + label: SCAppLocalizations.of(context)!.follow, + onTap: () { + if (widget.isMe == "true") { + SCNavigatorUtils.push(context, SCMainRoute.follow); + } + }, + ), + _buildHeaderStatsDivider(), + _buildHeaderStatItem( + value: "${counterMap["FANS"]?.quantity ?? 0}", + label: SCAppLocalizations.of(context)!.fans, + onTap: () { + if (widget.isMe == "true") { + SCNavigatorUtils.push(context, SCMainRoute.fans); + } + }, + ), + ], + ), + ); + } + + Widget _buildHeaderStatItem({ + required String value, + required String label, + required VoidCallback onTap, + }) { + return Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + text( + value, + fontSize: 17.sp, + textColor: Colors.white, + fontWeight: FontWeight.bold, + ), + text(label, fontSize: 14.sp, textColor: Color(0xffB1B1B1)), + ], + ), + ), + ); + } + + Widget _buildHeaderStatsDivider() { + return Container( + height: 28.w, + width: 1.w, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color.fromRGBO(255, 255, 255, 0), + Colors.white, + Color.fromRGBO(255, 255, 255, 0), + ], + stops: [0, 0.4862, 1], + ), + ), + ); + } + + Widget _buildGradientBorder({ + required double width, + required double height, + required double radius, + required Widget child, + Color backgroundColor = _cardBg, + double endAlpha = 0, + }) { + final borderRadius = BorderRadius.circular(radius); + return Container( + width: width, + height: height, + decoration: BoxDecoration( + borderRadius: borderRadius, + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [_profileBorder, _profileBorder.withValues(alpha: endAlpha)], + ), + ), + padding: EdgeInsets.all(1.w), + child: Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular((radius - 1.w).clamp(0, radius)), + ), + child: child, + ), + ); + } + Widget _buildProfileHeader( SocialChatUserProfileManager ref, List backgroundPhotos, ) { return SizedBox( - height: 450.w, + height: 520.w, child: Stack( children: [ SizedBox( @@ -450,7 +595,7 @@ class _PersonDetailPageState extends State left: 0, right: 0, bottom: 0, - child: Container(color: Color(0xff083b2f)), + child: Container(color: _profileBg), ), Positioned( top: 210.w, @@ -464,133 +609,13 @@ class _PersonDetailPageState extends State _buildHeaderName(ref), SizedBox(height: 5.w), _buildHeaderMeta(ref), - SizedBox(height: 5.w), - Consumer( - builder: (context, ref, child) { - return Container( - height: 72.w, - alignment: AlignmentDirectional.center, - width: ScreenUtil().screenWidth, - margin: EdgeInsets.symmetric(horizontal: 18.w), - padding: EdgeInsets.symmetric(horizontal: 35.w), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GestureDetector( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - text( - "${counterMap["INTERVIEW"]?.quantity ?? 0}", - fontSize: 17.sp, - textColor: Colors.white, - fontWeight: FontWeight.bold, - ), - text( - SCAppLocalizations.of(context)!.vistors, - fontSize: 14.sp, - textColor: Color(0xffB1B1B1), - ), - ], - ), - onTap: () { - if (widget.isMe == "true") { - SCNavigatorUtils.push( - context, - SCMainRoute.vistors, - ); - } - }, - ), - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Color(0xff333333).withOpacity(0.0), - Color(0xff333333), - Color(0xff333333).withOpacity(0.0), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - height: 25.w, - width: 1.w, - ), - GestureDetector( - onTap: () { - if (widget.isMe == "true") { - SCNavigatorUtils.push( - context, - SCMainRoute.follow, - ); - } - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - text( - "${counterMap["SUBSCRIPTION"]?.quantity ?? 0}", - fontSize: 17.sp, - textColor: Colors.white, - fontWeight: FontWeight.bold, - ), - text( - SCAppLocalizations.of(context)!.follow, - fontSize: 14.sp, - textColor: Color(0xffB1B1B1), - ), - ], - ), - ), - Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Color(0xff333333).withOpacity(0.0), - Color(0xff333333), - Color(0xff333333).withOpacity(0.0), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - height: 25.w, - width: 1.w, - ), - GestureDetector( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - text( - "${counterMap["FANS"]?.quantity ?? 0}", - fontSize: 17.sp, - textColor: Colors.white, - fontWeight: FontWeight.bold, - ), - text( - SCAppLocalizations.of(context)!.fans, - fontSize: 14.sp, - textColor: Color(0xffB1B1B1), - ), - ], - ), - onTap: () { - if (widget.isMe == "true") { - SCNavigatorUtils.push( - context, - SCMainRoute.fans, - ); - } - }, - ), - ], - ), - ); - }, - ), + SizedBox(height: 4.w), + _buildHeaderBadgePlaceholder(), SizedBox(height: 6.w), + _buildHeaderIntroduction(ref), + SizedBox(height: 12.w), + _buildHeaderStats(), + SizedBox(height: 10.w), ], ), ), @@ -599,13 +624,353 @@ class _PersonDetailPageState extends State ); } + Widget _buildProfileColumnContent() { + return Container( + width: double.infinity, + color: _profileBg, + padding: EdgeInsets.fromLTRB(10.w, 4.w, 10.w, 220.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildProfileSectionTitle("Wall Of Honors"), + SizedBox(height: 12.w), + _buildWallGrid( + itemCount: 4, + itemBuilder: (_) => _buildHonorPlaceholderItem(), + ), + SizedBox(height: 18.w), + _buildProfileSectionTitle( + "Gift Wall", + onTap: () { + SCNavigatorUtils.push( + context, + "${SCMainRoute.giftWall}?tageId=${widget.tageId}", + ); + }, + ), + SizedBox(height: 12.w), + _buildWallGrid( + itemCount: giftWallList.isEmpty ? 4 : giftWallList.length, + itemBuilder: (index) { + final gift = + index < giftWallList.length ? giftWallList[index] : null; + return _buildGiftWallItem(gift); + }, + ), + ], + ), + ); + } + + Widget _buildProfileSectionTitle(String title, {VoidCallback? onTap}) { + final titleAsset = + title == "Wall Of Honors" + ? "sc_images/person/sc_icon_profile_wall_of_honors_title.png" + : "sc_images/person/sc_icon_profile_gift_wall_title.png"; + final titleHeight = 32.h; + final titleWidth = title == "Wall Of Honors" ? 98.w : 61.w; + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: Row( + children: [ + Expanded( + child: Align( + alignment: AlignmentDirectional.centerStart, + child: Image.asset( + titleAsset, + height: titleHeight, + width: titleWidth, + fit: BoxFit.contain, + errorBuilder: + (_, __, ___) => Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: _profileBorder, + fontSize: 19.sp, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.w800, + ), + ), + ), + ), + ), + Icon(Icons.chevron_right_rounded, color: Colors.white, size: 28.w), + ], + ), + ); + } + + Widget _buildWallGrid({ + required int itemCount, + required Widget Function(int index) itemBuilder, + }) { + final spacing = + ((ScreenUtil().screenWidth - 20.w - 76.w * 4) / 3) + .clamp(8.w, 26.w) + .toDouble(); + return Wrap( + spacing: spacing, + runSpacing: 17.w, + children: List.generate(itemCount, itemBuilder), + ); + } + + Widget _buildHonorPlaceholderItem() { + return _buildWallItemFrame( + child: Opacity( + opacity: 0.45, + child: Image.asset( + "sc_images/room/sc_icon_room_music_empty.png", + width: 38.w, + height: 38.w, + fit: BoxFit.contain, + ), + ), + ); + } + + Widget _buildGiftWallItem(SocialChatGiftRes? gift) { + return _buildWallItemFrame( + child: Stack( + children: [ + Positioned.fill( + child: Padding( + padding: EdgeInsets.all(8.w), + child: + gift == null + ? Opacity( + opacity: 0.28, + child: Image.asset( + "sc_images/room/sc_icon_room_music_empty.png", + fit: BoxFit.contain, + ), + ) + : netImage( + url: gift.giftPhoto ?? "", + width: double.infinity, + height: double.infinity, + fit: BoxFit.contain, + noDefaultImg: true, + ), + ), + ), + if (gift != null) + Positioned( + right: 5.w, + bottom: 4.w, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 1.w), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(8.w), + ), + child: text( + "x${gift.quantity ?? 0}", + fontSize: 10.sp, + textColor: Colors.white, + ), + ), + ), + ], + ), + ); + } + + Widget _buildWallItemFrame({required Widget child}) { + return _buildGradientBorder( + width: 76.w, + height: 76.w, + radius: 8.w, + backgroundColor: _cardBg, + endAlpha: 0.62, + child: child, + ); + } + + Widget _buildBottomActions(SocialChatUserProfileManager ref) { + final hasRoom = (ref.userProfile?.inRoomId ?? "").isNotEmpty; + final safeBottom = MediaQuery.of(context).padding.bottom; + final bottomPadding = safeBottom + 32.w; + final actionRowBottom = bottomPadding; + final enterBottom = actionRowBottom + 36.w + 43.w; + final backgroundHeight = 93.w + safeBottom + 24.w; + final height = hasRoom ? enterBottom + 38.w : backgroundHeight; + + return SizedBox( + height: height, + child: Stack( + clipBehavior: Clip.none, + children: [ + Positioned( + left: 0, + right: 0, + bottom: 0, + child: Container(height: backgroundHeight, color: _profileBg), + ), + if (hasRoom) + Positioned( + left: 0, + right: 0, + bottom: enterBottom, + child: _buildEnterRoomPill(ref), + ), + Positioned( + left: 0, + right: 0, + bottom: actionRowBottom, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SCDebounceWidget( + debounceTime: const Duration(milliseconds: 800), + onTap: () { + SCAccountRepository().followUser(widget.tageId).then((v) { + if (!mounted) return; + isFollow = !isFollow; + setState(() {}); + }); + }, + child: _buildBottomButton( + width: 154.w, + icon: + isFollow + ? "sc_images/person/sc_icon_profile_following.png" + : "sc_images/person/sc_icon_person_follow.png", + label: + isFollow + ? SCAppLocalizations.of(context)!.following + : SCAppLocalizations.of(context)!.follow, + ), + ), + SizedBox(width: 20.w), + SCDebounceWidget( + debounceTime: const Duration(milliseconds: 800), + onTap: _openChat, + child: _buildBottomButton( + width: 165.w, + icon: "sc_images/person/sc_icon_person_tochat.png", + label: SCAppLocalizations.of(context)!.chat, + ), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildBottomButton({ + required double width, + required String icon, + required String label, + }) { + return Container( + width: width, + height: 36.w, + decoration: BoxDecoration( + color: const Color(0xff0A342D), + borderRadius: BorderRadius.circular(22.w), + border: Border.all(color: const Color(0xff18F2B1), width: 1.w), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset(icon, width: 28.w, height: 28.w, fit: BoxFit.contain), + SizedBox(width: 6.w), + Flexible( + child: text( + label, + fontSize: 16.sp, + textColor: Colors.white, + fontWeight: FontWeight.w400, + maxLines: 1, + ), + ), + ], + ), + ); + } + + Widget _buildEnterRoomPill(SocialChatUserProfileManager ref) { + return Center( + child: Container( + width: 343.w, + height: 38.w, + decoration: BoxDecoration( + color: const Color(0xff0A342D), + borderRadius: BorderRadius.circular(22.w), + border: Border.all(color: const Color(0xff18F2B1), width: 1.w), + ), + child: Row( + children: [ + SizedBox(width: 5.w), + head(url: ref.userProfile?.userAvatar ?? "", width: 30.w), + SizedBox(width: 8.w), + Expanded( + child: socialchatNickNameText( + ref.userProfile?.userNickname ?? "", + maxWidth: 190.w, + fontSize: 16.sp, + textColor: Colors.white, + fontWeight: FontWeight.w400, + type: ref.userProfile?.getVIP()?.name ?? "", + needScroll: + (ref.userProfile?.userNickname?.characters.length ?? 0) > + 16, + ), + ), + SCDebounceWidget( + debounceTime: const Duration(milliseconds: 800), + onTap: () { + SCRoomUtils.goRoom(ref.userProfile?.inRoomId ?? "", context); + }, + child: Container( + width: 69.w, + height: 27.w, + alignment: Alignment.center, + decoration: BoxDecoration( + color: const Color(0xff18F2B1), + borderRadius: BorderRadius.circular(22.w), + ), + child: text( + SCAppLocalizations.of(context)!.enter, + fontSize: 16.sp, + textColor: Colors.black, + fontWeight: FontWeight.w500, + ), + ), + ), + SizedBox(width: 8.w), + ], + ), + ), + ); + } + + Future _openChat() async { + final conversation = V2TimConversation( + type: ConversationType.V2TIM_C2C, + userID: widget.tageId, + conversationID: '', + ); + final started = await Provider.of( + context, + listen: false, + ).startConversation(conversation); + if (!started || !mounted) return; + final json = jsonEncode(conversation.toJson()); + SCNavigatorUtils.push( + context, + "${SCChatRouter.chat}?conversation=${Uri.encodeComponent(json)}", + ); + } + @override Widget build(BuildContext context) { - _tabs.clear(); - // _tabs.add(_buildTab(0, SCAppLocalizations.of(context)!.dynamicT)); - _tabs.add(_buildTab(0, SCAppLocalizations.of(context)!.aboutMe)); - _tabs.add(_buildTab(1, SCAppLocalizations.of(context)!.giftwall)); - // _tabs.add(_buildTab(3, SCAppLocalizations.of(context)!.relationShip)); return Consumer( builder: (context, ref, child) { final bool isProfileLoading = ref.userProfile == null; @@ -646,9 +1011,7 @@ class _PersonDetailPageState extends State fit: BoxFit.cover, ), ), - Expanded( - child: Container(color: const Color(0xff083b2f)), - ), + Expanded(child: Container(color: _profileBg)), ], ), ), @@ -982,126 +1345,14 @@ class _PersonDetailPageState extends State ? Container() : (isProfileLoading || isBlacklistLoading ? const SizedBox.shrink() - : ExtendedNestedScrollView( + : SingleChildScrollView( controller: _scrollController, - onlyOneScrollInBody: true, - headerSliverBuilder: ( - BuildContext context, - bool innerBoxIsScrolled, - ) { - return [ - SliverToBoxAdapter( - child: _buildProfileHeader( - ref, - backgroundPhotos, - ), - ), - ]; - }, - body: Container( - padding: EdgeInsets.symmetric( - horizontal: 3.w, - ), - decoration: BoxDecoration( - gradient: - ref.userProfile?.userSex == 0 - ? LinearGradient( - colors: [ - Color(0xff083b2f), - Color(0xff083b2f), - ], - begin: - AlignmentDirectional.topStart, - end: - AlignmentDirectional - .bottomEnd, - ) - : LinearGradient( - colors: [ - Color(0xff083b2f), - Color(0xff083b2f), - ], - begin: - AlignmentDirectional.topStart, - end: - AlignmentDirectional - .bottomEnd, - ), - ), - child: Column( - children: [ - Row( - children: [ - SizedBox(width: 6.w), - SizedBox( - height: 35.w, - child: TabBar( - tabAlignment: TabAlignment.start, - indicator: - SCFixedWidthTabIndicator( - width: 20.w, - height: 4.w, - gradient: - ref - .userProfile - ?.userSex == - 0 - ? LinearGradient( - colors: [ - Color( - 0xffFFD800, - ), - Color( - 0xffFFD800, - ), - ], - ) - : LinearGradient( - colors: [ - Color( - 0xffFFD800, - ), - Color( - 0xffFFD800, - ), - ], - ), - ), - labelPadding: - EdgeInsets.symmetric( - horizontal: 12.w, - ), - labelColor: Colors.white, - isScrollable: true, - unselectedLabelColor: Color( - 0xffB1B1B1, - ), - labelStyle: TextStyle( - fontSize: 15.sp, - fontWeight: FontWeight.bold, - ), - unselectedLabelStyle: TextStyle( - fontSize: 13.sp, - ), - indicatorColor: - Colors.transparent, - dividerColor: Colors.transparent, - controller: _tabController, - tabs: _tabs, - ), - ), - SizedBox(width: 6.w), - ], - ), - Expanded( - child: TabBarView( - controller: _tabController, - children: _pages, - ), - ), - SizedBox(height: 25.w), - ], - ), + physics: const BouncingScrollPhysics(), + child: Column( + children: [ + _buildProfileHeader(ref, backgroundPhotos), + _buildProfileColumnContent(), + ], ), )), ), @@ -1117,187 +1368,7 @@ class _PersonDetailPageState extends State ? Container() : (isLoading ? Container() - : Container( - alignment: AlignmentDirectional.center, - width: ScreenUtil().screenWidth, - height: 75.w, - decoration: BoxDecoration( - color: Color(0xff18F2B1).withOpacity(0.1), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10.w), - topRight: Radius.circular(10.w), - ), - ), - child: Column( - children: [ - SizedBox(height: 10.w), - Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - SCDebounceWidget( - debounceTime: Duration( - milliseconds: 800, - ), - child: SizedBox( - width: 110.w, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Image.asset( - "sc_images/person/sc_icon_person_in_room.png", - width: 23.w, - ), - SizedBox(height: 5.w), - Container( - margin: EdgeInsets.only( - bottom: 0.w, - ), - child: text( - SCAppLocalizations.of( - context, - )!.inRoom, - textColor: - SocialChatTheme - .primaryLight, - fontWeight: - FontWeight.w600, - fontSize: 14.sp, - ), - ), - ], - ), - ), - onTap: () { - if ((ref - .userProfile - ?.inRoomId ?? - "") - .isNotEmpty) { - SCRoomUtils.goRoom( - ref.userProfile?.inRoomId ?? - "", - context, - ); - } - }, - ), - SizedBox(width: 10.w), - SCDebounceWidget( - debounceTime: Duration( - milliseconds: 800, - ), - child: Container( - width: 110.w, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Image.asset( - "sc_images/person/sc_icon_person_tochat.png", - width: 23.w, - ), - SizedBox(height: 5.w), - Container( - margin: EdgeInsets.only( - bottom: 0.w, - ), - child: text( - SCAppLocalizations.of( - context, - )!.message, - textColor: - SocialChatTheme - .primaryLight, - fontWeight: - FontWeight.w600, - fontSize: 14.sp, - ), - ), - ], - ), - ), - onTap: () async { - var conversation = - V2TimConversation( - type: - ConversationType - .V2TIM_C2C, - userID: widget.tageId, - conversationID: '', - ); - var bool = await Provider.of< - RtmProvider - >( - context, - listen: false, - ).startConversation( - conversation, - ); - if (!bool) return; - var json = jsonEncode( - conversation.toJson(), - ); - SCNavigatorUtils.push( - context, - "${SCChatRouter.chat}?conversation=${Uri.encodeComponent(json)}", - ); - }, - ), - SizedBox(width: 10.w), - SCDebounceWidget( - debounceTime: Duration( - milliseconds: 800, - ), - child: Container( - width: 110.w, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Image.asset( - "sc_images/person/sc_icon_person_follow.png", - width: 23.w, - ), - SizedBox(height: 5.w), - Container( - margin: EdgeInsets.only( - bottom: 0.w, - ), - child: text( - isFollow - ? SCAppLocalizations.of( - context, - )!.following - : SCAppLocalizations.of( - context, - )!.follow, - textColor: - SocialChatTheme - .primaryLight, - fontWeight: - FontWeight.w600, - fontSize: 14.sp, - ), - ), - ], - ), - ), - onTap: () { - SCAccountRepository() - .followUser(widget.tageId) - .then((v) { - isFollow = !isFollow; - setState(() {}); - }); - }, - ), - ], - ), - ], - ), - )), + : _buildBottomActions(ref)), ), ], ), diff --git a/lib/modules/user/profile/profile_gift_wall_detail_page.dart b/lib/modules/user/profile/profile_gift_wall_detail_page.dart new file mode 100644 index 0000000..4439cc8 --- /dev/null +++ b/lib/modules/user/profile/profile_gift_wall_detail_page.dart @@ -0,0 +1,495 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; +import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/services/auth/user_profile_manager.dart'; +import 'package:yumi/shared/business_logic/models/res/gift_res.dart'; +import 'package:yumi/shared/business_logic/models/res/login_res.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/services/general/sc_app_general_manager.dart'; +import 'package:yumi/ui_kit/components/sc_compontent.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; + +class ProfileGiftWallDetailPage extends StatefulWidget { + const ProfileGiftWallDetailPage({super.key, required this.tageId}); + + final String tageId; + + @override + State createState() => + _ProfileGiftWallDetailPageState(); +} + +class _ProfileGiftWallDetailPageState extends State { + static const Color _profileBg = Color(0xff072121); + static const Color _cardBg = Color(0xff08251E); + static const Color _profileBorder = Color(0xffB2FBCC); + + SocialChatUserProfile? _profile; + List _gifts = []; + bool _loadingGifts = true; + + @override + void initState() { + super.initState(); + _primeProfile(); + _loadProfile(); + _loadGiftWall(); + } + + void _primeProfile() { + final currentProfile = + Provider.of( + context, + listen: false, + ).userProfile; + if (currentProfile?.id == widget.tageId) { + _profile = currentProfile; + } + } + + void _loadProfile() { + SCAccountRepository() + .loadUserInfo(widget.tageId) + .then((profile) { + if (!mounted) return; + setState(() { + _profile = profile; + }); + }) + .catchError((_) {}); + } + + Future _loadGiftWall() async { + final appGeneralManager = Provider.of( + context, + listen: false, + ); + try { + final result = await SCGiftRepositoryImp().giftWall(widget.tageId); + final resolvedGifts = await _resolveGiftWallMeta( + result, + appGeneralManager, + ); + if (!mounted) return; + setState(() { + _gifts = resolvedGifts; + _loadingGifts = false; + }); + } catch (_) { + if (!mounted) return; + setState(() { + _loadingGifts = false; + }); + } + } + + Future> _resolveGiftWallMeta( + List gifts, + SCAppGeneralManager appGeneralManager, + ) async { + if (gifts.isEmpty) return gifts; + if (appGeneralManager.giftResList.isEmpty) { + await appGeneralManager.giftList(includeCustomized: false); + } + return gifts + .map((gift) => _mergeGiftMeta(gift, appGeneralManager)) + .toList(); + } + + SocialChatGiftRes _mergeGiftMeta( + SocialChatGiftRes gift, + SCAppGeneralManager appGeneralManager, + ) { + final meta = _findGiftMeta(gift, appGeneralManager); + if (meta == null) return gift; + return gift.copyWith( + giftName: _firstNonBlank([gift.giftName, meta.giftName]), + giftCandy: gift.giftCandy ?? meta.giftCandy, + giftIntegral: gift.giftIntegral ?? meta.giftIntegral, + giftPhoto: _firstNonBlank([gift.giftPhoto, meta.giftPhoto]), + giftSourceUrl: _firstNonBlank([gift.giftSourceUrl, meta.giftSourceUrl]), + giftCode: _firstNonBlank([gift.giftCode, meta.giftCode]), + standardId: _firstNonBlank([gift.standardId, meta.standardId]), + ); + } + + SocialChatGiftRes? _findGiftMeta( + SocialChatGiftRes gift, + SCAppGeneralManager appGeneralManager, + ) { + final idCandidates = [ + gift.standardId ?? "", + gift.id ?? "", + gift.giftCode ?? "", + ].where((id) => id.trim().isNotEmpty && id.trim() != "0"); + + for (final id in idCandidates) { + final meta = appGeneralManager.getGiftByIdOrStandardId(id); + if (meta != null) return meta; + } + + final giftPhoto = (gift.giftPhoto ?? "").trim(); + final giftCode = (gift.giftCode ?? "").trim(); + for (final meta in appGeneralManager.giftResList) { + if (giftPhoto.isNotEmpty && giftPhoto == (meta.giftPhoto ?? "").trim()) { + return meta; + } + if (giftCode.isNotEmpty && giftCode == (meta.giftCode ?? "").trim()) { + return meta; + } + } + return null; + } + + String? _firstNonBlank(Iterable values) { + for (final value in values) { + final text = value?.trim(); + if (text != null && text.isNotEmpty) { + return text; + } + } + return null; + } + + int get _receivedCount { + return _gifts.fold(0, (sum, gift) => sum + _giftQuantity(gift)); + } + + num get _totalValue { + return _gifts.fold( + 0, + (sum, gift) => sum + (_giftAmount(gift) * _giftQuantity(gift)), + ); + } + + int _giftQuantity(SocialChatGiftRes gift) { + return int.tryParse(gift.quantity ?? "") ?? 0; + } + + num _giftAmount(SocialChatGiftRes gift) { + return gift.giftCandy ?? gift.giftIntegral ?? 0; + } + + String _formatNumber(num value) { + if (value % 1 == 0) return value.toInt().toString(); + return value.toStringAsFixed(1); + } + + Widget _buildGradientBorder({ + double? width, + double? height, + required double radius, + required Widget child, + Color backgroundColor = _cardBg, + double endAlpha = 0.42, + }) { + final borderRadius = BorderRadius.circular(radius); + return Container( + width: width, + height: height, + decoration: BoxDecoration( + borderRadius: borderRadius, + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [_profileBorder, _profileBorder.withValues(alpha: endAlpha)], + ), + ), + padding: EdgeInsets.all(1.w), + child: Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular((radius - 1.w).clamp(0, radius)), + ), + child: child, + ), + ); + } + + Widget _buildTopBar() { + return SizedBox( + width: double.infinity, + height: 54.w, + child: Stack( + alignment: Alignment.center, + children: [ + PositionedDirectional( + start: 8.w, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => SCNavigatorUtils.goBack(context), + child: SizedBox( + width: 52.w, + height: 52.w, + child: Icon( + Icons.keyboard_arrow_left_rounded, + color: Colors.white, + size: 38.w, + ), + ), + ), + ), + Text( + "Gift Wall", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: Colors.white, + fontSize: 19.sp, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ); + } + + Widget _buildProfileHeader() { + return Column( + children: [ + SizedBox(height: 12.w), + head( + url: _profile?.userAvatar ?? "", + width: 104.w, + headdress: _profile?.getHeaddress()?.sourceUrl, + ), + SizedBox(height: 10.w), + socialchatNickNameText( + _profile?.userNickname ?? "", + maxWidth: 300.w, + fontSize: 20, + textColor: Colors.white, + fontWeight: FontWeight.w400, + type: _profile?.getVIP()?.name ?? "", + needScroll: (_profile?.userNickname?.characters.length ?? 0) > 13, + ), + SizedBox(height: 18.w), + _buildSummaryCard(), + ], + ); + } + + Widget _buildSummaryCard() { + return _buildGradientBorder( + width: 355.w, + height: 51.w, + radius: 8.w, + backgroundColor: _cardBg, + endAlpha: 0.52, + child: Row( + children: [ + _buildSummaryItem( + value: _formatNumber(_receivedCount), + label: "Received", + ), + _buildSummaryDivider(), + _buildSummaryItem( + value: _formatNumber(_totalValue), + label: "Total Value", + showCoin: true, + ), + ], + ), + ); + } + + Widget _buildSummaryItem({ + required String value, + required String label, + bool showCoin = false, + }) { + return Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (showCoin) ...[_buildCoinIcon(14.w), SizedBox(width: 3.w)], + text( + value, + fontSize: 17, + textColor: Colors.white, + fontWeight: FontWeight.bold, + ), + ], + ), + text(label, fontSize: 14, textColor: const Color(0xffB1B1B1)), + ], + ), + ); + } + + Widget _buildSummaryDivider() { + return Container( + height: 28.w, + width: 1.w, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color.fromRGBO(255, 255, 255, 0), + Colors.white, + Color.fromRGBO(255, 255, 255, 0), + ], + stops: [0, 0.4862, 1], + ), + ), + ); + } + + Widget _buildGiftGrid() { + if (_loadingGifts) { + return SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.only(top: 80.w), + child: Center( + child: SizedBox( + width: 24.w, + height: 24.w, + child: const CircularProgressIndicator( + strokeWidth: 2, + color: _profileBorder, + ), + ), + ), + ), + ); + } + + if (_gifts.isEmpty) { + return SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.only(top: 90.w), + child: Opacity( + opacity: 0.42, + child: Image.asset( + "sc_images/room/sc_icon_room_music_empty.png", + width: 72.w, + height: 72.w, + fit: BoxFit.contain, + ), + ), + ), + ); + } + + return SliverPadding( + padding: EdgeInsets.fromLTRB(10.w, 34.w, 10.w, 0), + sliver: SliverGrid( + delegate: SliverChildBuilderDelegate( + (context, index) => _buildGiftItem(_gifts[index]), + childCount: _gifts.length, + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 25.w, + crossAxisSpacing: 22.w, + childAspectRatio: 104 / 130, + ), + ), + ); + } + + Widget _buildGiftItem(SocialChatGiftRes gift) { + final quantity = _giftQuantity(gift); + final giftName = + (gift.giftName ?? "").trim().isEmpty ? "--" : gift.giftName!.trim(); + return _buildGradientBorder( + radius: 8.w, + backgroundColor: _cardBg, + endAlpha: 0.62, + child: Padding( + padding: EdgeInsets.fromLTRB(10.w, 10.w, 10.w, 8.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Center( + child: netImage( + url: gift.giftPhoto ?? "", + width: 82.w, + height: 74.w, + fit: BoxFit.contain, + noDefaultImg: true, + ), + ), + ), + SizedBox(height: 7.w), + text( + "$giftName*$quantity", + fontSize: 13, + textColor: Colors.white, + maxLines: 1, + ), + SizedBox(height: 5.w), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildCoinIcon(12.w), + SizedBox(width: 4.w), + Flexible( + child: text( + _formatNumber(_giftAmount(gift)), + fontSize: 11, + textColor: Colors.white, + maxLines: 1, + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget _buildCoinIcon(double size) { + return SizedBox( + width: size, + height: size, + child: ClipRect( + child: FittedBox( + fit: BoxFit.cover, + alignment: Alignment.bottomCenter, + child: Image.asset("sc_images/room/sc_icon_luckgift_coins_anim.webp"), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + final bottomPadding = MediaQuery.of(context).padding.bottom + 22.w; + return Scaffold( + backgroundColor: _profileBg, + body: SafeArea( + bottom: false, + child: CustomScrollView( + physics: const BouncingScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: Column(children: [_buildTopBar(), _buildProfileHeader()]), + ), + _buildGiftGrid(), + SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.only(top: 54.w, bottom: bottomPadding), + child: Center( + child: text( + "No Further Data Available~", + fontSize: 13, + textColor: const Color(0xffB1B1B1), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/shared/business_logic/models/res/gift_res.dart b/lib/shared/business_logic/models/res/gift_res.dart index a35d53d..2cef234 100644 --- a/lib/shared/business_logic/models/res/gift_res.dart +++ b/lib/shared/business_logic/models/res/gift_res.dart @@ -1,182 +1,250 @@ -/// id : 0 -/// giftCode : "" -/// giftPhoto : "" -/// giftSourceUrl : "" -/// giftCandy : 0.0 -/// giftIntegral : 0.0 -/// special : "" -/// type : "" -/// giftTab : "" -/// standardId : 0 -/// explanationGift : false -/// account : "" -/// userId : 0 -/// giftName : "" -/// expiredTime : 0 - -class SocialChatGiftRes { - SocialChatGiftRes({ - String? id, - String? giftCode, - String? giftPhoto, - String? giftSourceUrl, - num? giftCandy, - num? activityId, - num? giftIntegral, - String? special, - String? type, - String? giftTab, - String? standardId, - String? jumpUrl, - String? bannerUrl, - bool? explanationGift, - String? account, - String? userId, - String? giftName, - String? quantity, - num? expiredTime,}){ - _id = id; - _giftCode = giftCode; - _giftPhoto = giftPhoto; - _giftSourceUrl = giftSourceUrl; - _giftCandy = giftCandy; - _activityId = activityId; - _giftIntegral = giftIntegral; - _special = special; - _type = type; - _giftTab = giftTab; - _standardId = standardId; - _jumpUrl = jumpUrl; - _bannerUrl = bannerUrl; - _explanationGift = explanationGift; - _account = account; - _userId = userId; - _giftName = giftName; - _quantity = quantity; - _expiredTime = expiredTime; -} - - SocialChatGiftRes.fromJson(dynamic json) { - _id = json['id']; - _giftCode = json['giftCode']; - _giftPhoto = json['giftPhoto']; - _giftSourceUrl = json['giftSourceUrl']; - _giftCandy = json['giftCandy']; - _activityId = json['activityId']; - _giftIntegral = json['giftIntegral']; - _special = json['special']; - _type = json['type']; - _giftTab = json['giftTab']; - _standardId = json['standardId']; - _jumpUrl = json['jumpUrl']; - _bannerUrl = json['bannerUrl']; - _explanationGift = json['explanationGift']; - _account = json['account']; - _userId = json['userId']; - _giftName = json['giftName']; - _quantity = json['quantity']; - _expiredTime = json['expiredTime']; - } - String? _id; - String? _giftCode; - String? _giftPhoto; - String? _giftSourceUrl; - num? _giftCandy; - num? _activityId; - num? _giftIntegral; - String? _special; - String? _type; - String? _giftTab; - String? _standardId; - String? _jumpUrl; - String? _bannerUrl; - bool? _explanationGift; - String? _account; - String? _userId; - String? _giftName; - String? _quantity; - num? _expiredTime; -SocialChatGiftRes copyWith({ String? id, - String? giftCode, - String? giftPhoto, - String? giftSourceUrl, - num? giftCandy, - num? activityId, - num? giftIntegral, - String? special, - String? type, - String? giftTab, - String? standardId, - String? jumpUrl, - String? bannerUrl, - bool? explanationGift, - String? account, - String? userId, - String? giftName, - String? quantity, - num? expiredTime, -}) => SocialChatGiftRes( id: id ?? _id, - giftCode: giftCode ?? _giftCode, - giftPhoto: giftPhoto ?? _giftPhoto, - giftSourceUrl: giftSourceUrl ?? _giftSourceUrl, - giftCandy: giftCandy ?? _giftCandy, - activityId: activityId ?? _activityId, - giftIntegral: giftIntegral ?? _giftIntegral, - special: special ?? _special, - type: type ?? _type, - giftTab: giftTab ?? _giftTab, - standardId: standardId ?? _standardId, - jumpUrl: jumpUrl ?? _jumpUrl, - bannerUrl: bannerUrl ?? _bannerUrl, - explanationGift: explanationGift ?? _explanationGift, - account: account ?? _account, - userId: userId ?? _userId, - giftName: giftName ?? _giftName, - quantity: quantity ?? _quantity, - expiredTime: expiredTime ?? _expiredTime, -); - String? get id => _id; - String? get giftCode => _giftCode; - String? get giftPhoto => _giftPhoto; - String? get giftSourceUrl => _giftSourceUrl; - num? get giftCandy => _giftCandy; - num? get activityId => _activityId; - num? get giftIntegral => _giftIntegral; - String? get special => _special; - String? get type => _type; - String? get giftTab => _giftTab; - String? get standardId => _standardId; - String? get jumpUrl => _jumpUrl; - String? get bannerUrl => _bannerUrl; - bool? get explanationGift => _explanationGift; - String? get account => _account; - String? get userId => _userId; - String? get giftName => _giftName; - String? get quantity => _quantity; - num? get expiredTime => _expiredTime; - - Map toJson() { - final map = {}; - map['id'] = _id; - map['giftCode'] = _giftCode; - map['giftPhoto'] = _giftPhoto; - map['giftSourceUrl'] = _giftSourceUrl; - map['giftCandy'] = _giftCandy; - map['activityId'] = _activityId; - map['giftIntegral'] = _giftIntegral; - map['special'] = _special; - map['type'] = _type; - map['giftTab'] = _giftTab; - map['standardId'] = _standardId; - map['jumpUrl'] = _jumpUrl; - map['bannerUrl'] = _bannerUrl; - map['explanationGift'] = _explanationGift; - map['account'] = _account; - map['userId'] = _userId; - map['giftName'] = _giftName; - map['quantity'] = _quantity; - map['expiredTime'] = _expiredTime; - return map; - } - -} \ No newline at end of file +// id : 0 +// giftCode : "" +// giftPhoto : "" +// giftSourceUrl : "" +// giftCandy : 0.0 +// giftIntegral : 0.0 +// special : "" +// type : "" +// giftTab : "" +// standardId : 0 +// explanationGift : false +// account : "" +// userId : 0 +// giftName : "" +// expiredTime : 0 + +class SocialChatGiftRes { + SocialChatGiftRes({ + String? id, + String? giftCode, + String? giftPhoto, + String? giftSourceUrl, + num? giftCandy, + num? activityId, + num? giftIntegral, + String? special, + String? type, + String? giftTab, + String? standardId, + String? jumpUrl, + String? bannerUrl, + bool? explanationGift, + String? account, + String? userId, + String? giftName, + String? quantity, + num? expiredTime, + }) { + _id = id; + _giftCode = giftCode; + _giftPhoto = giftPhoto; + _giftSourceUrl = giftSourceUrl; + _giftCandy = giftCandy; + _activityId = activityId; + _giftIntegral = giftIntegral; + _special = special; + _type = type; + _giftTab = giftTab; + _standardId = standardId; + _jumpUrl = jumpUrl; + _bannerUrl = bannerUrl; + _explanationGift = explanationGift; + _account = account; + _userId = userId; + _giftName = giftName; + _quantity = quantity; + _expiredTime = expiredTime; + } + + SocialChatGiftRes.fromJson(dynamic json) { + final giftConfig = + _giftResMap(json['giftConfig']) ?? + _giftResMap(json['gift']) ?? + _giftResMap(json['giftInfo']) ?? + _giftResMap(json['giftVO']); + + _id = _giftResString(json['id'] ?? giftConfig?['id']); + _giftCode = _giftResString(json['giftCode'] ?? giftConfig?['giftCode']); + _giftPhoto = _giftResString( + json['giftPhoto'] ?? + json['giftCover'] ?? + json['cover'] ?? + giftConfig?['giftPhoto'] ?? + giftConfig?['giftCover'] ?? + giftConfig?['cover'], + ); + _giftSourceUrl = _giftResString( + json['giftSourceUrl'] ?? + json['sourceUrl'] ?? + giftConfig?['giftSourceUrl'] ?? + giftConfig?['sourceUrl'], + ); + _giftCandy = _giftResNum( + json['giftCandy'] ?? + json['giftAmount'] ?? + json['giftValue'] ?? + json['amount'] ?? + json['price'] ?? + json['value'] ?? + json['coins'] ?? + giftConfig?['giftCandy'] ?? + giftConfig?['giftAmount'] ?? + giftConfig?['giftValue'] ?? + giftConfig?['amount'] ?? + giftConfig?['price'] ?? + giftConfig?['value'] ?? + giftConfig?['coins'], + ); + _activityId = _giftResNum(json['activityId'] ?? giftConfig?['activityId']); + _giftIntegral = _giftResNum( + json['giftIntegral'] ?? + json['integral'] ?? + giftConfig?['giftIntegral'] ?? + giftConfig?['integral'], + ); + _special = _giftResString(json['special'] ?? giftConfig?['special']); + _type = _giftResString(json['type'] ?? giftConfig?['type']); + _giftTab = _giftResString(json['giftTab'] ?? giftConfig?['giftTab']); + _standardId = _giftResString( + json['standardId'] ?? giftConfig?['standardId'], + ); + _jumpUrl = _giftResString(json['jumpUrl'] ?? giftConfig?['jumpUrl']); + _bannerUrl = _giftResString(json['bannerUrl'] ?? giftConfig?['bannerUrl']); + _explanationGift = json['explanationGift']; + _account = _giftResString(json['account'] ?? giftConfig?['account']); + _userId = _giftResString(json['userId'] ?? giftConfig?['userId']); + _giftName = _giftResString( + json['giftName'] ?? + json['name'] ?? + giftConfig?['giftName'] ?? + giftConfig?['name'], + ); + _quantity = _giftResString( + json['quantity'] ?? json['giftQuantity'] ?? json['count'] ?? json['num'], + ); + _expiredTime = _giftResNum( + json['expiredTime'] ?? giftConfig?['expiredTime'], + ); + } + String? _id; + String? _giftCode; + String? _giftPhoto; + String? _giftSourceUrl; + num? _giftCandy; + num? _activityId; + num? _giftIntegral; + String? _special; + String? _type; + String? _giftTab; + String? _standardId; + String? _jumpUrl; + String? _bannerUrl; + bool? _explanationGift; + String? _account; + String? _userId; + String? _giftName; + String? _quantity; + num? _expiredTime; + SocialChatGiftRes copyWith({ + String? id, + String? giftCode, + String? giftPhoto, + String? giftSourceUrl, + num? giftCandy, + num? activityId, + num? giftIntegral, + String? special, + String? type, + String? giftTab, + String? standardId, + String? jumpUrl, + String? bannerUrl, + bool? explanationGift, + String? account, + String? userId, + String? giftName, + String? quantity, + num? expiredTime, + }) => SocialChatGiftRes( + id: id ?? _id, + giftCode: giftCode ?? _giftCode, + giftPhoto: giftPhoto ?? _giftPhoto, + giftSourceUrl: giftSourceUrl ?? _giftSourceUrl, + giftCandy: giftCandy ?? _giftCandy, + activityId: activityId ?? _activityId, + giftIntegral: giftIntegral ?? _giftIntegral, + special: special ?? _special, + type: type ?? _type, + giftTab: giftTab ?? _giftTab, + standardId: standardId ?? _standardId, + jumpUrl: jumpUrl ?? _jumpUrl, + bannerUrl: bannerUrl ?? _bannerUrl, + explanationGift: explanationGift ?? _explanationGift, + account: account ?? _account, + userId: userId ?? _userId, + giftName: giftName ?? _giftName, + quantity: quantity ?? _quantity, + expiredTime: expiredTime ?? _expiredTime, + ); + String? get id => _id; + String? get giftCode => _giftCode; + String? get giftPhoto => _giftPhoto; + String? get giftSourceUrl => _giftSourceUrl; + num? get giftCandy => _giftCandy; + num? get activityId => _activityId; + num? get giftIntegral => _giftIntegral; + String? get special => _special; + String? get type => _type; + String? get giftTab => _giftTab; + String? get standardId => _standardId; + String? get jumpUrl => _jumpUrl; + String? get bannerUrl => _bannerUrl; + bool? get explanationGift => _explanationGift; + String? get account => _account; + String? get userId => _userId; + String? get giftName => _giftName; + String? get quantity => _quantity; + num? get expiredTime => _expiredTime; + + Map toJson() { + final map = {}; + map['id'] = _id; + map['giftCode'] = _giftCode; + map['giftPhoto'] = _giftPhoto; + map['giftSourceUrl'] = _giftSourceUrl; + map['giftCandy'] = _giftCandy; + map['activityId'] = _activityId; + map['giftIntegral'] = _giftIntegral; + map['special'] = _special; + map['type'] = _type; + map['giftTab'] = _giftTab; + map['standardId'] = _standardId; + map['jumpUrl'] = _jumpUrl; + map['bannerUrl'] = _bannerUrl; + map['explanationGift'] = _explanationGift; + map['account'] = _account; + map['userId'] = _userId; + map['giftName'] = _giftName; + map['quantity'] = _quantity; + map['expiredTime'] = _expiredTime; + return map; + } +} + +Map? _giftResMap(dynamic value) { + if (value is Map) return value; + if (value is Map) return Map.from(value); + return null; +} + +String? _giftResString(dynamic value) { + if (value == null) return null; + return value.toString(); +} + +num? _giftResNum(dynamic value) { + if (value == null) return null; + if (value is num) return value; + return num.tryParse(value.toString()); +} diff --git a/sc_images/person/sc_icon_profile_following.png b/sc_images/person/sc_icon_profile_following.png new file mode 100644 index 0000000000000000000000000000000000000000..01fe7630e248328e47ee43952e709cedd518ca32 GIT binary patch literal 5316 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 UhEqK9rvLx|07*qoM6N<$g4b`qfB*mh literal 0 HcmV?d00001 diff --git a/sc_images/person/sc_icon_profile_wall_of_honors_title.png b/sc_images/person/sc_icon_profile_wall_of_honors_title.png new file mode 100644 index 0000000000000000000000000000000000000000..961ba280110a7ac9a581c6357c03c54e284f3bff GIT binary patch literal 13881 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