diff --git a/lib/modules/auth/edit/sc_edit_profile_page.dart b/lib/modules/auth/edit/sc_edit_profile_page.dart index 232af8c..2e9677a 100644 --- a/lib/modules/auth/edit/sc_edit_profile_page.dart +++ b/lib/modules/auth/edit/sc_edit_profile_page.dart @@ -1,18 +1,18 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:yumi/app_localizations.dart'; -import 'package:yumi/ui_kit/components/sc_compontent.dart'; -import 'package:yumi/ui_kit/components/text/sc_text.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:yumi/app_localizations.dart'; +import 'package:yumi/ui_kit/components/sc_compontent.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; import 'package:yumi/ui_kit/components/sc_tts.dart'; import 'package:yumi/shared/tools/sc_loading_manager.dart'; import 'package:yumi/shared/tools/sc_pick_utils.dart'; -import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; -import 'package:yumi/services/auth/authentication_manager.dart'; -import 'package:provider/provider.dart'; -import 'package:yumi/app/constants/sc_screen.dart'; -import 'package:yumi/app/routes/sc_routes.dart'; -import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart'; +import 'package:yumi/services/auth/authentication_manager.dart'; +import 'package:provider/provider.dart'; +import 'package:yumi/app/constants/sc_screen.dart'; +import 'package:yumi/app/routes/sc_routes.dart'; +import 'package:yumi/app/routes/sc_fluro_navigator.dart'; import 'package:yumi/shared/tools/sc_date_utils.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; import 'package:yumi/shared/business_logic/models/res/login_res.dart'; @@ -21,9 +21,9 @@ import 'package:yumi/services/auth/user_profile_manager.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import 'package:yumi/services/audio/rtm_manager.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; - -import '../../../shared/business_logic/usecases/sc_custom_filtering_textinput_formatter.dart'; -import '../../country/country_route.dart'; + +import '../../../shared/business_logic/usecases/sc_custom_filtering_textinput_formatter.dart'; +import '../../country/country_route.dart'; ///编辑个人信息 class SCEditProfilePage extends StatefulWidget { @@ -33,7 +33,7 @@ class SCEditProfilePage extends StatefulWidget { _SCEditProfilePageState createState() => _SCEditProfilePageState(); } -class _SCEditProfilePageState extends State { +class _SCEditProfilePageState extends State { ///默认女 int type = 0; DateTime birthdayDate = DateTime(2006); @@ -484,7 +484,7 @@ class _SCEditProfilePageState extends State { ); } - DateTime _getBefor18() { + DateTime _getBefor18() { DateTime currentDate = DateTime.now(); DateTime eighteenYearsAgo = DateTime( currentDate.year - 18, @@ -496,31 +496,49 @@ class _SCEditProfilePageState extends State { currentDate.millisecond, currentDate.microsecond, ); - return eighteenYearsAgo; - } - - String? _preferNonEmpty(String? primary, String? fallback) { - if (primary != null && primary.isNotEmpty) { - return primary; - } - if (fallback != null && fallback.isNotEmpty) { - return fallback; - } - return primary ?? fallback; - } - - num? _preferNonZero(num? primary, num? fallback) { - if (primary != null && primary != 0) { - return primary; - } - if (fallback != null && fallback != 0) { - return fallback; - } - return primary ?? fallback; - } - - ///提交数据 - void _submitUserProfile() async { + return eighteenYearsAgo; + } + + String? _preferNonEmpty(String? primary, String? fallback) { + if (primary != null && primary.isNotEmpty) { + return primary; + } + if (fallback != null && fallback.isNotEmpty) { + return fallback; + } + return primary ?? fallback; + } + + bool _isBrokenLocalMediaUrl(String? url) { + return (url ?? "").contains("/external/oss/local/"); + } + + String? _preferUsableAvatar(String? primary, String? fallback) { + if (primary != null && + primary.isNotEmpty && + !_isBrokenLocalMediaUrl(primary)) { + return primary; + } + if (fallback != null && + fallback.isNotEmpty && + !_isBrokenLocalMediaUrl(fallback)) { + return fallback; + } + return _preferNonEmpty(primary, fallback); + } + + num? _preferNonZero(num? primary, num? fallback) { + if (primary != null && primary != 0) { + return primary; + } + if (fallback != null && fallback != 0) { + return fallback; + } + return primary ?? fallback; + } + + ///提交数据 + void _submitUserProfile() async { if (nicknameController.text.isEmpty) { SCTts.show(SCAppLocalizations.of(context)!.pleaseEnterNickname); return; @@ -555,67 +573,67 @@ class _SCEditProfilePageState extends State { context, listen: false, ).uid; - SocialChatLoginRes user = await SCAccountRepository().regist( - authType, - idToken, - userProvider!.editUser!, - ); - final submittedProfile = userProvider?.editUser; - if (submittedProfile != null) { - final mergedProfile = - (user.userProfile ?? SocialChatUserProfile()).copyWith( - userAvatar: _preferNonEmpty( - user.userProfile?.userAvatar, - submittedProfile.userAvatar, - ), - userNickname: _preferNonEmpty( - user.userProfile?.userNickname, - submittedProfile.userNickname, - ), - userSex: _preferNonZero( - user.userProfile?.userSex, - submittedProfile.userSex, - ), - bornYear: _preferNonZero( - user.userProfile?.bornYear, - submittedProfile.bornYear, - ), - bornMonth: _preferNonZero( - user.userProfile?.bornMonth, - submittedProfile.bornMonth, - ), - bornDay: _preferNonZero( - user.userProfile?.bornDay, - submittedProfile.bornDay, - ), - age: _preferNonZero(user.userProfile?.age, submittedProfile.age), - countryCode: _preferNonEmpty( - user.userProfile?.countryCode, - submittedProfile.countryCode, - ), - countryId: _preferNonEmpty( - user.userProfile?.countryId, - submittedProfile.countryId, - ), - countryName: _preferNonEmpty( - user.userProfile?.countryName, - submittedProfile.countryName, - ), - ); - user.setUserProfile(mergedProfile); - } - if (!mounted) { - return; - } + SocialChatLoginRes user = await SCAccountRepository().regist( + authType, + idToken, + userProvider!.editUser!, + ); + final submittedProfile = userProvider?.editUser; + if (submittedProfile != null) { + final mergedProfile = (user.userProfile ?? SocialChatUserProfile()) + .copyWith( + userAvatar: _preferUsableAvatar( + user.userProfile?.userAvatar, + submittedProfile.userAvatar, + ), + userNickname: _preferNonEmpty( + user.userProfile?.userNickname, + submittedProfile.userNickname, + ), + userSex: _preferNonZero( + user.userProfile?.userSex, + submittedProfile.userSex, + ), + bornYear: _preferNonZero( + user.userProfile?.bornYear, + submittedProfile.bornYear, + ), + bornMonth: _preferNonZero( + user.userProfile?.bornMonth, + submittedProfile.bornMonth, + ), + bornDay: _preferNonZero( + user.userProfile?.bornDay, + submittedProfile.bornDay, + ), + age: _preferNonZero(user.userProfile?.age, submittedProfile.age), + countryCode: _preferNonEmpty( + user.userProfile?.countryCode, + submittedProfile.countryCode, + ), + countryId: _preferNonEmpty( + user.userProfile?.countryId, + submittedProfile.countryId, + ), + countryName: _preferNonEmpty( + user.userProfile?.countryName, + submittedProfile.countryName, + ), + ); + user.setUserProfile(mergedProfile); + } + if (!mounted) { + return; + } await Provider.of(context, listen: false).resetLocalRoomState( fallbackRtmProvider: Provider.of(context, listen: false), ); if (!mounted) { return; - } - AccountStorage().setCurrentUser(user); - userProvider?.syncCurrentUserProfile(user.userProfile); - SCLoadingManager.hide(); - SCNavigatorUtils.push(context, SCRoutes.home, clearStack: true); - } -} + } + AccountStorage().setCurrentUser(user); + userProvider?.syncCurrentUserProfile(user.userProfile); + SCLoadingManager.hide(); + SCNavigatorUtils.push(context, SCRoutes.home, clearStack: true); + } +} diff --git a/lib/modules/room/edit/room_edit_page.dart b/lib/modules/room/edit/room_edit_page.dart index 1a32b1c..3a7cfa0 100644 --- a/lib/modules/room/edit/room_edit_page.dart +++ b/lib/modules/room/edit/room_edit_page.dart @@ -14,9 +14,11 @@ import 'package:yumi/ui_kit/components/appbar/socialchat_appbar.dart'; import 'package:yumi/ui_kit/components/dialog/dialog_base.dart'; import 'package:yumi/app/constants/sc_screen.dart'; import 'package:yumi/app/routes/sc_fluro_navigator.dart'; +import 'package:yumi/app/constants/sc_room_msg_type.dart'; import 'package:yumi/shared/tools/sc_pick_utils.dart'; import 'package:yumi/services/room/rc_room_manager.dart'; import 'package:yumi/services/audio/rtm_manager.dart'; +import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart'; import '../../../shared/tools/sc_lk_dialog_util.dart'; import '../../../shared/data_sources/models/enum/sc_room_info_event_type.dart'; import '../../../shared/business_logic/usecases/sc_accurate_length_limiting_textInput_formatter.dart'; @@ -545,6 +547,21 @@ class _RoomEditPageState extends State { roomDesc: mergedRoomInfo.roomDesc, ); roomManager.updateMyRoomInfo(mergedRoomInfo); + if (widget.needRestCurrentRoomInfo != "true") { + currentRtmProvider.dispatchMessage( + Msg( + groupId: + rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount ?? + "", + msg: + mergedRoomInfo.id ?? + rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? + "", + type: SCRoomMsgType.roomSettingUpdate, + ), + addLocal: false, + ); + } if (widget.needRestCurrentRoomInfo == "true") { ///需要创建群组 var c = await currentRtmProvider.createRoomGroup( diff --git a/lib/modules/user/edit/edit_user_info_page2.dart b/lib/modules/user/edit/edit_user_info_page2.dart index afd13fe..50c36fc 100644 --- a/lib/modules/user/edit/edit_user_info_page2.dart +++ b/lib/modules/user/edit/edit_user_info_page2.dart @@ -85,6 +85,31 @@ class _EditUserInfoPage2State extends State super.dispose(); } + String? _preferNonEmpty(String? primary, String? fallback) { + if ((primary ?? "").trim().isNotEmpty) { + return primary; + } + if ((fallback ?? "").trim().isNotEmpty) { + return fallback; + } + return primary ?? fallback; + } + + bool _isBrokenLocalMediaUrl(String? url) { + return (url ?? "").contains("/external/oss/local/"); + } + + String? _preferUsableAvatar(String? primary, String? fallback) { + if ((primary ?? "").trim().isNotEmpty && !_isBrokenLocalMediaUrl(primary)) { + return primary; + } + if ((fallback ?? "").trim().isNotEmpty && + !_isBrokenLocalMediaUrl(fallback)) { + return fallback; + } + return _preferNonEmpty(primary, fallback); + } + @override Widget build(BuildContext context) { return Stack( @@ -123,10 +148,13 @@ class _EditUserInfoPage2State extends State children: [ netImage( url: - UserManager() - .getCurrentUser() - ?.userProfile - ?.userAvatar ?? + _preferNonEmpty( + userCover, + UserManager() + .getCurrentUser() + ?.userProfile + ?.userAvatar, + ) ?? "", width: 45.w, height: 45.w, @@ -143,9 +171,15 @@ class _EditUserInfoPage2State extends State ], ), onTap: () { - SCPickUtils.pickImage(context, (bool success, String url) { + SCPickUtils.pickImage(context, ( + bool success, + String url, + ) { if (success) { - submit(context); + debugPrint("[Profile Avatar] uploaded url: $url"); + userCover = url; + setState(() {}); + submitAvatarOnly(context); } }); }, @@ -153,8 +187,7 @@ class _EditUserInfoPage2State extends State SizedBox(height: 3.w), _buildItem( "${SCAppLocalizations.of(context)!.userName}:", - nickName ?? - "", + nickName, () { _showInputBioHobby(nickName, 3); }, @@ -315,6 +348,50 @@ class _EditUserInfoPage2State extends State int sTime = 0; + void submitAvatarOnly(BuildContext context) async { + if ((userCover ?? "").trim().isEmpty) { + return; + } + int bTime = DateTime.now().millisecondsSinceEpoch; + if (bTime - sTime <= 5000) { + return; + } + sTime = bTime; + SCLoadingManager.show(); + debugPrint("[Profile Avatar] submit avatar only: {userAvatar: $userCover}"); + try { + final updatedProfile = await SCAccountRepository().updateUserInfo( + userAvatar: userCover, + ); + final mergedProfile = updatedProfile.copyWith( + userAvatar: _preferUsableAvatar(updatedProfile.userAvatar, userCover), + ); + debugPrint( + "[Profile Avatar] merged avatar-only profile avatar: ${mergedProfile.userAvatar ?? ""}", + ); + userCover = mergedProfile.userAvatar; + if (!mounted) { + SCLoadingManager.hide(); + return; + } + Provider.of( + context, + listen: false, + ).syncCurrentUserProfile(mergedProfile); + Provider.of(context, listen: false).needUpDataUserInfo = + true; + SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful); + } catch (e) { + debugPrint(e.toString()); + SCLoadingManager.hide(); + return; + } + SCLoadingManager.hide(); + if (mounted) { + setState(() {}); + } + } + void submit(BuildContext context) async { if (nickName.isEmpty) { SCTts.show(SCAppLocalizations.of(context)!.pleaseEnterNickname); @@ -323,40 +400,67 @@ class _EditUserInfoPage2State extends State int bTime = DateTime.now().millisecondsSinceEpoch; if (bTime - sTime > 5000) { sTime = bTime; - SCLoadingManager.show(); - try { - final updatedProfile = await SCAccountRepository().updateUserInfo( - userAvatar: userCover, - userSex: sex, - userNickname: nickName, - age: age, + SCLoadingManager.show(); + debugPrint( + "[Profile Avatar] submit payload: {userAvatar: $userCover, userNickname: $nickName, userSex: $sex, age: $age, bornYear: ${birthdayDate?.year}, bornMonth: ${birthdayDate?.month}, bornDay: ${birthdayDate?.day}, countryId: ${country?.id}, hobby: $hobby, autograph: $autograph}", + ); + try { + final updatedProfile = await SCAccountRepository().updateUserInfo( + userAvatar: userCover, + userSex: sex, + userNickname: nickName, + age: age, bornDay: birthdayDate?.day, bornMonth: birthdayDate?.month, bornYear: birthdayDate?.year, - hobby: hobby, - autograph: autograph, - countryId: country?.id, - ); - if (!mounted) { - SCLoadingManager.hide(); - return; - } - Provider.of( - context, - listen: false, - ).syncCurrentUserProfile(updatedProfile); - Provider.of(context, listen: false).needUpDataUserInfo = - true; - SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful); - } catch (e) { + hobby: hobby, + autograph: autograph, + countryId: country?.id, + ); + final mergedProfile = updatedProfile.copyWith( + userAvatar: _preferUsableAvatar(updatedProfile.userAvatar, userCover), + userNickname: _preferNonEmpty(updatedProfile.userNickname, nickName), + autograph: _preferNonEmpty(updatedProfile.autograph, autograph), + hobby: _preferNonEmpty(updatedProfile.hobby, hobby), + countryName: _preferNonEmpty( + updatedProfile.countryName, + country?.countryName, + ), + countryCode: _preferNonEmpty( + updatedProfile.countryCode, + country?.alphaTwo, + ), + countryId: _preferNonEmpty(updatedProfile.countryId, country?.id), + userSex: updatedProfile.userSex ?? sex, + age: updatedProfile.age ?? age, + bornDay: updatedProfile.bornDay ?? birthdayDate?.day, + bornMonth: updatedProfile.bornMonth ?? birthdayDate?.month, + bornYear: updatedProfile.bornYear ?? birthdayDate?.year, + ); + debugPrint( + "[Profile Avatar] merged profile avatar: ${mergedProfile.userAvatar ?? ""}", + ); + userCover = mergedProfile.userAvatar; + if (!mounted) { + SCLoadingManager.hide(); + return; + } + Provider.of( + context, + listen: false, + ).syncCurrentUserProfile(mergedProfile); + Provider.of(context, listen: false).needUpDataUserInfo = + true; + SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful); + } catch (e) { debugPrint(e.toString()); SCLoadingManager.hide(); + return; } - Provider.of( - context, - listen: false, - ).fetchUserProfileData(); SCLoadingManager.hide(); + if (mounted) { + setState(() {}); + } } } @@ -451,11 +555,7 @@ class _EditUserInfoPage2State extends State Expanded( child: Container( alignment: AlignmentDirectional.centerEnd, - child: text( - value, - textColor: Colors.white, - fontSize: 15.sp, - ), + child: text(value, textColor: Colors.white, fontSize: 15.sp), ), ), Icon( @@ -530,20 +630,22 @@ class _EditUserInfoPage2State extends State ], ), onTap: () { - SCNavigatorUtils.push(context, CountryRoute.country, replace: false).then( - (res) { - var c = - Provider.of( - navigatorKey.currentState!.context, - listen: false, - ).selectCountryInfo; - if (c != null) { - country = c; - submit(navigatorKey.currentState!.context); - setState(() {}); - } - }, - ); + SCNavigatorUtils.push( + context, + CountryRoute.country, + replace: false, + ).then((res) { + var c = + Provider.of( + navigatorKey.currentState!.context, + listen: false, + ).selectCountryInfo; + if (c != null) { + country = c; + submit(navigatorKey.currentState!.context); + setState(() {}); + } + }); }, ); } @@ -608,10 +710,7 @@ class _EditUserInfoPage2State extends State decoration: InputDecoration( isDense: true, hintText: "", - hintStyle: TextStyle( - color: Colors.white, - fontSize: 14.sp, - ), + hintStyle: TextStyle(color: Colors.white, fontSize: 14.sp), contentPadding: EdgeInsets.only(top: 0.w), counterText: '', filled: false, @@ -662,7 +761,7 @@ class _EditUserInfoPage2State extends State } else if (type == 2) { hobby = _inputController.text; setState(() {}); - }else if (type == 3) { + } else if (type == 3) { nickName = _inputController.text; setState(() {}); } diff --git a/lib/shared/data_sources/sources/repositories/sc_general_repository_imp.dart b/lib/shared/data_sources/sources/repositories/sc_general_repository_imp.dart index 42ab55d..3e2cf39 100644 --- a/lib/shared/data_sources/sources/repositories/sc_general_repository_imp.dart +++ b/lib/shared/data_sources/sources/repositories/sc_general_repository_imp.dart @@ -21,6 +21,8 @@ class SCGeneralRepositoryImp implements SocialChatGeneralRepository { filePath.lastIndexOf("/") + 1, filePath.length, ); + debugPrint("[上传头像图片地址] filePath: $filePath"); + debugPrint("[上传头像图片] fileName: $name"); FormData formData = FormData.fromMap({ "file": await MultipartFile.fromFile(filePath, filename: name), }); @@ -38,8 +40,8 @@ class SCGeneralRepositoryImp implements SocialChatGeneralRepository { response.data as Map, fromJsonT: (json) => json as String, ); - debugPrint("[OSS Upload] response.data: ${response.data}"); - debugPrint("[OSS Upload] parsed file url: ${baseResponse.body ?? ""}"); + debugPrint("[返回值] response.data: ${response.data}"); + debugPrint("[返回图片地址] parsed file url: ${baseResponse.body ?? ""}"); return baseResponse.body ?? ""; } } diff --git a/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart b/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart index e70d4fe..e0e3c0c 100644 --- a/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart +++ b/lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart @@ -14,7 +14,6 @@ import 'package:yumi/shared/business_logic/models/res/sc_room_reward_info_res.da import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_config_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_room_rocket_status_res.dart'; import 'package:yumi/shared/business_logic/models/res/sc_room_task_list_res.dart'; -import 'package:yumi/shared/business_logic/models/res/sc_top_four_with_reward_res.dart'; import 'package:yumi/shared/business_logic/models/res/user_count_guard_res.dart'; import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart'; import 'package:provider/provider.dart'; @@ -45,6 +44,45 @@ class SCChatRoomRepository implements SocialChatRoomRepository { return _instance ??= SCChatRoomRepository._internal(); } + bool _isBrokenLocalMediaUrl(String? url) { + return (url ?? "").contains("/external/oss/local/"); + } + + bool _shouldHydrateRoomProfile(String? roomId, String? roomCover) { + return (roomId ?? "").trim().isNotEmpty && + ((roomCover ?? "").trim().isEmpty || _isBrokenLocalMediaUrl(roomCover)); + } + + Future _hydrateRoomRes(SocialChatRoomRes room) async { + final cachedRoom = SCRoomProfileCache.applyToSocialChatRoomRes(room); + if (!_shouldHydrateRoomProfile(cachedRoom.id, cachedRoom.roomCover)) { + return cachedRoom; + } + debugPrint( + "[Room Cover][room-list] hydrate roomId=${cachedRoom.id} remoteCover=${cachedRoom.roomCover ?? ""}", + ); + final specificRoom = await specific(cachedRoom.id ?? ""); + debugPrint( + "[Room Cover][room-list] specific roomId=${cachedRoom.id} specificCover=${specificRoom.roomCover ?? ""}", + ); + return SCRoomProfileCache.applyToSocialChatRoomRes( + cachedRoom.copyWith( + roomCover: SCRoomProfileCache.preferNonEmpty( + specificRoom.roomCover, + cachedRoom.roomCover, + ), + roomName: SCRoomProfileCache.preferNonEmpty( + specificRoom.roomName, + cachedRoom.roomName, + ), + roomDesc: SCRoomProfileCache.preferNonEmpty( + specificRoom.roomDesc, + cachedRoom.roomDesc, + ), + ), + ); + } + ///room/live-voice/discovery @override Future> discovery({bool? allRegion}) async { @@ -59,7 +97,7 @@ class SCChatRoomRepository implements SocialChatRoomRepository { (json) => (json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(), ); - return result.map(SCRoomProfileCache.applyToSocialChatRoomRes).toList(); + return Future.wait(result.map(_hydrateRoomRes)); } ///live/mic/list @@ -86,6 +124,15 @@ class SCChatRoomRepository implements SocialChatRoomRepository { queryParams: queryParams, fromJson: (json) => MyRoomRes.fromJson(json), ); + debugPrint( + "[Room Cover][specific] roomId=${result.id ?? roomId} roomCover=${result.roomCover ?? ""} roomName=${result.roomName ?? ""}", + ); + await SCRoomProfileCache.saveRoomProfile( + roomId: result.id ?? roomId, + roomCover: result.roomCover, + roomName: result.roomName, + roomDesc: result.roomDesc, + ); return SCRoomProfileCache.applyToMyRoomRes(result); } @@ -328,7 +375,7 @@ class SCChatRoomRepository implements SocialChatRoomRepository { (json) => (json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(), ); - return result.map(SCRoomProfileCache.applyToSocialChatRoomRes).toList(); + return Future.wait(result.map(_hydrateRoomRes)); } ///room/blacklist/join diff --git a/lib/ui_kit/components/sc_compontent.dart b/lib/ui_kit/components/sc_compontent.dart index 61c9031..b2356d6 100644 --- a/lib/ui_kit/components/sc_compontent.dart +++ b/lib/ui_kit/components/sc_compontent.dart @@ -1,684 +1,719 @@ -import 'package:extended_image/extended_image.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:yumi/ui_kit/components/text/sc_text.dart'; -import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; -import 'package:tancent_vap/utils/constant.dart'; -import 'package:tancent_vap/widgets/vap_view.dart'; -import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart'; -import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart'; -import 'package:yumi/app/constants/sc_global_config.dart'; -import 'package:yumi/app/constants/sc_screen.dart'; -import 'package:yumi/shared/tools/sc_path_utils.dart'; -import 'package:yumi/shared/tools/sc_room_profile_cache.dart'; - -import '../../shared/data_sources/models/enum/sc_room_roles_type.dart'; - -const String kRoomCoverDefaultImg = "sc_images/general/sc_no_data.png"; - -String resolveRoomCoverUrl(String? roomId, String? roomCover) { - final cache = SCRoomProfileCache.getRoomProfile(roomId ?? ""); - return SCRoomProfileCache.preferCachedValue(cache["roomCover"], roomCover) ?? - ""; -} - -Widget head({ - required String url, - required double width, - double? height, - Border? border, - String? headdress, - BoxShape shape = BoxShape.circle, - BoxFit fit = BoxFit.cover, - BorderRadius? borderRadius, - bool showDefault = true, - bool isRoom = false, -}) { - final bool hasPictureHeaddress = - headdress != null && - headdress.isNotEmpty && - SCPathUtils.fileTypeIsPic2(headdress); - final double avatarScale = - hasPictureHeaddress - ? 0.64 - : (headdress != null && headdress.isNotEmpty ? 0.72 : 1); - final double avatarWidth = width * avatarScale; - final double avatarHeight = (height ?? width) * avatarScale; - - Widget avatar = SizedBox( - width: avatarWidth, - height: avatarHeight, - child: netImage( - url: url, - width: avatarWidth, - height: avatarHeight, - fit: fit, - noDefaultImg: !showDefault, - defaultImg: "sc_images/general/sc_icon_avar_defalt.png", - ), - ); - - if (shape == BoxShape.circle) { - avatar = ClipOval(child: avatar); - if (border != null) { - avatar = Container( - width: avatarWidth, - height: avatarHeight, - decoration: ShapeDecoration(shape: CircleBorder(side: border.top)), - clipBehavior: Clip.antiAlias, - child: avatar, - ); - } - } else if (borderRadius != null) { - avatar = ClipRRect(borderRadius: borderRadius, child: avatar); - if (border != null) { - avatar = Container( - width: avatarWidth, - height: avatarHeight, - decoration: BoxDecoration(border: border, borderRadius: borderRadius), - clipBehavior: Clip.antiAlias, - child: avatar, - ); - } - } else if (border != null) { - avatar = Container( - width: avatarWidth, - height: avatarHeight, - decoration: BoxDecoration(border: border), - clipBehavior: Clip.antiAlias, - child: avatar, - ); - } - - return RepaintBoundary( - child: Stack( - alignment: Alignment.center, - children: [ - avatar, - hasPictureHeaddress - ? ColorFiltered( - colorFilter: const ColorFilter.matrix([ - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - 0, - -1, - -1, - -1, - 3, - 0, - ]), - child: netImage( - url: headdress, - width: width, - height: height ?? width, - fit: BoxFit.contain, - noDefaultImg: true, - ), - ) - : Container(), - headdress != null && - SCPathUtils.getFileExtension(headdress).toLowerCase() == - ".svga" && - SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim - ? IgnorePointer( - child: SVGAHeadwearWidget( - width: width, - height: width, - resource: headdress, - ), - ) - : Container(), - headdress != null && - SCPathUtils.getFileExtension(headdress).toLowerCase() == - ".mp4" && - SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim - ? SizedBox( - width: width, - height: width, - child: IgnorePointer( - child: VapView( - scaleType: ScaleType.centerCrop, - repeat: 90000000, - onViewCreated: (controller) { - FileCacheManager.getInstance().getFile(url: headdress).then( - (file) { - controller.playFile(file.path); - }, - ); - }, - ), - ), - ) - : Container(), - ], - ), - ); -} - -Widget netImage({ - required String url, - String? defaultImg, - bool noDefaultImg = false, - double? width, - double? height, - BorderRadius? borderRadius, - BoxFit? fit, - BoxShape? shape, - Border? border, -}) { - // print('${SCGlobalConfig.imgHost}$image?imageslim'); - return ExtendedImage.network( - url, - width: width, - height: height ?? width, - fit: fit ?? BoxFit.cover, - cache: true, - shape: shape ?? BoxShape.rectangle, - borderRadius: borderRadius, - clearMemoryCacheWhenDispose: false, - clearMemoryCacheIfFailed: true, - border: border, - gaplessPlayback: true, - loadStateChanged: (ExtendedImageState state) { - if (state.extendedImageLoadState == LoadState.completed) { - return ExtendedRawImage( - image: state.extendedImageInfo?.image, - fit: fit ?? BoxFit.cover, - ); - } else if (state.extendedImageLoadState == LoadState.failed) { - return noDefaultImg - ? Container() - : Image.asset( - defaultImg ?? "sc_images/general/sc_icon_loading.png", - fit: BoxFit.cover, - ); - } else { - return noDefaultImg - ? Container() - : Image.asset( - defaultImg ?? "sc_images/general/sc_icon_loading.png", - fit: BoxFit.cover, - ); - } - }, - ); -} - -/// 空页面 -///默认空页面 -mainEmpty({ - String msg = "No data", - Widget? image, - bool center = true, - Color textColor = const Color(0xffffffff), -}) { - List list = []; - list.add(SizedBox(height: height(center ? 40.w : 0.w))); - list.add( - image ?? - Image.asset( - 'sc_images/general/sc_icon_loading.png', - width: 121.w, - fit: BoxFit.fitWidth, - ), - ); - list.add(SizedBox(height: height(10.w))); - list.add( - Text( - msg, - style: TextStyle( - fontSize: sp(12), - color: textColor, - fontWeight: FontWeight.w400, - decoration: TextDecoration.none, - height: 1, - ), - ), - ); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: - center ? MainAxisAlignment.center : MainAxisAlignment.start, - children: list, - ), - ], - ); -} - -msgRoleTag(String role, {double? width, double? height}) { - String image = ""; - if (SCRoomRolesType.HOMEOWNER.name == role) { - image = 'fz'; - } else if (SCRoomRolesType.ADMIN.name == role) { - image = "gly"; - } else if (SCRoomRolesType.MEMBER.name == role) { - image = "hy"; - } else if (SCRoomRolesType.TOURIST.name == role) { - image = "guest"; - } - if (image.isEmpty) return SizedBox.shrink(); - return Container( - alignment: Alignment.center, - width: width ?? 22.w, - height: height ?? 22.w, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("sc_images/room/sc_icon_room_$image.png"), - ), - ), - ); -} - -///性别 0女 1男 -xb(num? sex, {double? height, Color? color = Colors.white}) { - // print('sex:$sex'); - // if (sex == null) return Container(); - String image = "sc_images/login/sc_icon_sex_man.png"; - if (sex == 0) { - image = "sc_images/login/sc_icon_sex_woman.png"; - } - return Image.asset( - image, - height: height ?? 14.w, - fit: BoxFit.fill, - color: color, - ); -} - -xb2(num? sex, {double? width, double? height}) { - // print('sex:$sex'); - if (sex == null) return Container(); - String image = "sc_images/login/sc_icon_sex_man_bg.png"; - if (sex == 0) { - image = "sc_images/login/sc_icon_sex_woman_bg.png"; - } - return Image.asset( - image, - width: width ?? 28.w, - height: height ?? 14.w, - fit: BoxFit.fill, - ); -} - -///搜索框 -searchWidget({ - Function? onChange, - Function? onTap, - String? hint, - double? hintSize, - Color? borderColor, - EdgeInsetsGeometry? padding, - TextEditingController? controller, - Color? textColor, - Color? serachIconColor, - FocusNode? focusNode, -}) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (onTap != null) { - onTap(); - } - }, - child: Container( - margin: - padding ?? - EdgeInsetsDirectional.only(start: width(15), end: width(0)), - alignment: AlignmentDirectional.centerStart, - height: width(36), - //width: width(345), - decoration: BoxDecoration( - color: Color(0xff18F2B1).withOpacity(0.1), - borderRadius: BorderRadius.all(Radius.circular(height(20))), - border: Border.all(color: borderColor ?? Colors.black12, width: 1.w), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox(width: width(10)), - Image.asset( - "sc_images/index/sc_icon_serach2.png", - width: 20.w, - color: serachIconColor ?? Color(0xff18F2B1), - height: 20.w, - ), - SizedBox(width: width(6)), - _buildPhoneInput( - onChange, - onTap == null, - hint ?? 'Search by ID', - controller, - textColor, - hintSize, - focusNode, - ), - ], - ), - ), - ); -} - -_buildPhoneInput( - Function? onChange, - bool enabled, - String tint, - TextEditingController? controller, - Color? textColor, - double? hintSize, - FocusNode? focusNode, -) { - return Expanded( - child: TextField( - controller: controller, - onChanged: (text) { - if (onChange != null) { - onChange(text); - } - }, - focusNode: focusNode, - autofocus: false, - enabled: enabled, - maxLength: 11, - cursorColor: SocialChatTheme.primaryColor, - decoration: InputDecoration( - hintText: tint, - hintStyle: TextStyle( - color: SocialChatTheme.textSecondary, - fontSize: hintSize ?? sp(14), - ), - counterText: '', - isDense: true, - filled: false, - focusColor: Colors.transparent, - hoverColor: Colors.transparent, - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - disabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - focusedErrorBorder: InputBorder.none, - contentPadding: EdgeInsets.only(bottom: 0), - ), - style: TextStyle( - textBaseline: TextBaseline.alphabetic, - fontSize: hintSize ?? sp(14), - color: textColor ?? Colors.black, - ), - ), - ); -} - -///vip标识 -getVIPBadge(String? type, {double? width, double? height}) { - return Container(); -} - -///用户等级 -getUserLevel(num level, {double? width, double? height, double? fontSize}) { - String icon = ""; - if (level > 0 && level < 11) { - icon = "sc_images/level/sc_icon_user_level_1_10.png"; - } else if (level > 10 && level < 21) { - icon = "sc_images/level/sc_icon_user_level_10_20.png"; - } else if (level > 20 && level < 31) { - icon = "sc_images/level/sc_icon_user_level_20_30.png"; - } else if (level > 30 && level < 41) { - icon = "sc_images/level/sc_icon_user_level_30_40.png"; - } else if (level > 40 && level < 51) { - icon = "sc_images/level/sc_icon_user_level_40_50.png"; - } - return icon.isNotEmpty - ? Directionality( - textDirection: TextDirection.ltr, - child: Stack( - alignment: AlignmentDirectional.center, - children: [ - Image.asset(icon, width: width, height: height, fit: BoxFit.fill), - PositionedDirectional( - end: level > 9 ? 10.w : 15.w, - child: text( - "$level", - fontSize: fontSize ?? 12.sp, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ) - : Container(); -} - -///用户魅力等级 -getWealthLevel(num level, {double? width, double? height, double? fontSize}) { - String icon = ""; - if (level > 0 && level < 11) { - icon = "sc_images/level/sc_icon_wealth_level_1_10.png"; - } else if (level > 10 && level < 21) { - icon = "sc_images/level/sc_icon_wealth_level_10_20.png"; - } else if (level > 20 && level < 31) { - icon = "sc_images/level/sc_icon_wealth_level_20_30.png"; - } else if (level > 30 && level < 41) { - icon = "sc_images/level/sc_icon_wealth_level_30_40.png"; - } else if (level > 40 && level < 51) { - icon = "sc_images/level/sc_icon_wealth_level_40_50.png"; - } - return icon.isNotEmpty - ? Directionality( - textDirection: TextDirection.ltr, - child: Stack( - alignment: AlignmentDirectional.center, - children: [ - Image.asset(icon, width: width, height: height, fit: BoxFit.fill), - PositionedDirectional( - end: level > 9 ? 10.w : 15.w, - child: text( - "$level", - fontSize: fontSize ?? 12.sp, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ) - : Container(); -} - -String getGiftObtCoinsBg(num obtCoins) { - String icon = ""; - if (obtCoins > -1 && obtCoins < 5000) { - icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_0.png"; - } else if (obtCoins > 4999 && obtCoins < 50000) { - icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_1.png"; - } else if (obtCoins > 49999 && obtCoins < 100000) { - icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_2.png"; - } else if (obtCoins > 99999 && obtCoins < 1000000) { - icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_3.png"; - } else if (obtCoins > 999999 && obtCoins < 10000000) { - icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_4.png"; - } else if (obtCoins > 9999999) { - icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_5.png"; - } - return icon; -} - -///数字图片 -buildNum(String num, {double? size = 18}) { - if (num.isNotEmpty) { - List eList = []; - int index = 0; - num.split("").forEach((item) { - if (item == "k") { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.9).w, - ), - child: Image.asset( - "sc_images/room/sc_icon_k.png", - width: size, - height: size, - ), - ), - ); - } else if (item == "m") { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.9).w, - ), - child: Image.asset( - "sc_images/room/sc_icon_m.png", - width: size, - height: size, - ), - ), - ); - } else { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.8).w, - ), - child: Image.asset( - "sc_images/room/sc_icon_number_$item.png", - width: size, - height: size, - ), - ), - ); - } - index = index + 1; - }); - return Directionality( - textDirection: TextDirection.ltr, - child: Stack(children: eList), - ); - } else { - return Container(); - } -} - -///游戏飘屏数字图片 -buildNumForGame(String num, {double? size = 18}) { - if (num.isNotEmpty) { - List eList = []; - int index = 0; - num.split("").forEach((item) { - if (item == "k") { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.65).w, - ), - child: Image.asset( - "sc_images/general/sc_icon_game_numk.png", - width: size, - height: size, - ), - ), - ); - } else if (item == "m") { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.65).w, - ), - child: Image.asset( - "sc_images/general/sc_icon_game_numm.png", - width: size, - height: size, - ), - ), - ); - } else { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.6).w, - ), - child: Image.asset( - "sc_images/general/sc_icon_game_num$item.png", - width: size, - height: size, - ), - ), - ); - } - index = index + 1; - }); - return Directionality( - textDirection: TextDirection.ltr, - child: Stack(children: eList), - ); - } else { - return Container(); - } -} - -///房间幸运数字 -buildNumForRoomLuckNum(String num, {double? size = 18}) { - if (num.isNotEmpty) { - List eList = []; - int index = 0; - num.split("").forEach((item) { - eList.add( - Container( - margin: EdgeInsetsDirectional.only( - start: (index * (size ?? 18) * 0.7).w, - ), - child: Image.asset( - "sc_images/room/sc_icon_lucknumber_$item.png", - width: size, - height: size, - ), - ), - ); - index = index + 1; - }); - return Directionality( - textDirection: TextDirection.ltr, - child: Stack(children: eList), - ); - } else { - return Container(); - } -} - -buildFamilyProgressLinearGradient(int level) { - if (level == 0) { - return LinearGradient(colors: [Color(0xff2E4957), Color(0xffC9E7E8)]); - } else if (level == 1) { - return LinearGradient(colors: [Color(0xff5A3F31), Color(0xffF8CD85)]); - } else if (level == 2) { - return LinearGradient(colors: [Color(0xff0D5ACE), Color(0xffCBD9E8)]); - } else if (level == 3) { - return LinearGradient(colors: [Color(0xffC57E1C), Color(0xffCBD9E8)]); - } else if (level == 10) { - return LinearGradient(colors: [Color(0xff3C7FBE), Color(0xffADE1F1)]); - } else if (level == 11) { - return LinearGradient(colors: [Color(0xff300A67), Color(0xffA53EFF)]); - } - return LinearGradient(colors: [Color(0xff2E4957), Color(0xffC9E7E8)]); -} - -buildVipNameColor(String type) { - return null; -} +import 'dart:io'; + +import 'package:extended_image/extended_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:yumi/ui_kit/components/text/sc_text.dart'; +import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; +import 'package:tancent_vap/utils/constant.dart'; +import 'package:tancent_vap/widgets/vap_view.dart'; +import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart'; +import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; +import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart'; +import 'package:yumi/app/constants/sc_global_config.dart'; +import 'package:yumi/app/constants/sc_screen.dart'; +import 'package:yumi/shared/tools/sc_path_utils.dart'; +import 'package:yumi/shared/tools/sc_room_profile_cache.dart'; + +import '../../shared/data_sources/models/enum/sc_room_roles_type.dart'; + +const String kRoomCoverDefaultImg = "sc_images/general/sc_no_data.png"; + +bool _requiresAuthenticatedImageHeaders(String url) { + return url.contains("/external/oss/local/"); +} + +Map? _buildImageHeaders(String url) { + if (!_requiresAuthenticatedImageHeaders(url)) { + return null; + } + final uri = Uri.tryParse(url); + final headers = { + "req-lang": SCGlobalConfig.lang, + "X-Forwarded-Proto": "http", + "User-Agent": "Dart/3.7.2(dart:io)", + "req-imei": SCGlobalConfig.imei, + "req-app-intel": + "version=${SCGlobalConfig.version};build=${SCGlobalConfig.build};model=${SCGlobalConfig.model};sysVersion=${SCGlobalConfig.sysVersion};channel=${SCGlobalConfig.channel}", + "req-client": Platform.isAndroid ? "Android" : "iOS", + "req-sys-origin": + "origin=${SCGlobalConfig.origin};originChild=${SCGlobalConfig.originChild}", + }; + if ((uri?.host ?? "").isNotEmpty) { + headers["Host"] = uri!.host; + } + final token = AccountStorage().getToken(); + if (token.isNotEmpty) { + headers["Authorization"] = "Bearer $token"; + } + headers.removeWhere((key, value) => value.trim().isEmpty); + return headers; +} + +String resolveRoomCoverUrl(String? roomId, String? roomCover) { + final cache = SCRoomProfileCache.getRoomProfile(roomId ?? ""); + return SCRoomProfileCache.preferCachedValue(cache["roomCover"], roomCover) ?? + ""; +} + +Widget head({ + required String url, + required double width, + double? height, + Border? border, + String? headdress, + BoxShape shape = BoxShape.circle, + BoxFit fit = BoxFit.cover, + BorderRadius? borderRadius, + bool showDefault = true, + bool isRoom = false, +}) { + final bool hasPictureHeaddress = + headdress != null && + headdress.isNotEmpty && + SCPathUtils.fileTypeIsPic2(headdress); + final double avatarScale = + hasPictureHeaddress + ? 0.64 + : (headdress != null && headdress.isNotEmpty ? 0.72 : 1); + final double avatarWidth = width * avatarScale; + final double avatarHeight = (height ?? width) * avatarScale; + + Widget avatar = SizedBox( + width: avatarWidth, + height: avatarHeight, + child: netImage( + url: url, + width: avatarWidth, + height: avatarHeight, + fit: fit, + noDefaultImg: !showDefault, + defaultImg: "sc_images/general/sc_icon_avar_defalt.png", + ), + ); + + if (shape == BoxShape.circle) { + avatar = ClipOval(child: avatar); + if (border != null) { + avatar = Container( + width: avatarWidth, + height: avatarHeight, + decoration: ShapeDecoration(shape: CircleBorder(side: border.top)), + clipBehavior: Clip.antiAlias, + child: avatar, + ); + } + } else if (borderRadius != null) { + avatar = ClipRRect(borderRadius: borderRadius, child: avatar); + if (border != null) { + avatar = Container( + width: avatarWidth, + height: avatarHeight, + decoration: BoxDecoration(border: border, borderRadius: borderRadius), + clipBehavior: Clip.antiAlias, + child: avatar, + ); + } + } else if (border != null) { + avatar = Container( + width: avatarWidth, + height: avatarHeight, + decoration: BoxDecoration(border: border), + clipBehavior: Clip.antiAlias, + child: avatar, + ); + } + + return RepaintBoundary( + child: Stack( + alignment: Alignment.center, + children: [ + avatar, + hasPictureHeaddress + ? ColorFiltered( + colorFilter: const ColorFilter.matrix([ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + -1, + -1, + -1, + 3, + 0, + ]), + child: netImage( + url: headdress, + width: width, + height: height ?? width, + fit: BoxFit.contain, + noDefaultImg: true, + ), + ) + : Container(), + headdress != null && + SCPathUtils.getFileExtension(headdress).toLowerCase() == + ".svga" && + SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim + ? IgnorePointer( + child: SVGAHeadwearWidget( + width: width, + height: width, + resource: headdress, + ), + ) + : Container(), + headdress != null && + SCPathUtils.getFileExtension(headdress).toLowerCase() == + ".mp4" && + SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim + ? SizedBox( + width: width, + height: width, + child: IgnorePointer( + child: VapView( + scaleType: ScaleType.centerCrop, + repeat: 90000000, + onViewCreated: (controller) { + FileCacheManager.getInstance().getFile(url: headdress).then( + (file) { + controller.playFile(file.path); + }, + ); + }, + ), + ), + ) + : Container(), + ], + ), + ); +} + +Widget netImage({ + required String url, + String? defaultImg, + bool noDefaultImg = false, + double? width, + double? height, + BorderRadius? borderRadius, + BoxFit? fit, + BoxShape? shape, + Border? border, +}) { + // print('${SCGlobalConfig.imgHost}$image?imageslim'); + return ExtendedImage.network( + url, + width: width, + height: height ?? width, + headers: _buildImageHeaders(url), + fit: fit ?? BoxFit.cover, + cache: true, + shape: shape ?? BoxShape.rectangle, + borderRadius: borderRadius, + clearMemoryCacheWhenDispose: false, + clearMemoryCacheIfFailed: true, + border: border, + gaplessPlayback: true, + loadStateChanged: (ExtendedImageState state) { + if (state.extendedImageLoadState == LoadState.completed) { + return ExtendedRawImage( + image: state.extendedImageInfo?.image, + fit: fit ?? BoxFit.cover, + ); + } else if (state.extendedImageLoadState == LoadState.failed) { + return noDefaultImg + ? Container() + : Image.asset( + defaultImg ?? "sc_images/general/sc_icon_loading.png", + fit: BoxFit.cover, + ); + } else { + return noDefaultImg + ? Container() + : Image.asset( + defaultImg ?? "sc_images/general/sc_icon_loading.png", + fit: BoxFit.cover, + ); + } + }, + ); +} + +/// 空页面 +///默认空页面 +mainEmpty({ + String msg = "No data", + Widget? image, + bool center = true, + Color textColor = const Color(0xffffffff), +}) { + List list = []; + list.add(SizedBox(height: height(center ? 40.w : 0.w))); + list.add( + image ?? + Image.asset( + 'sc_images/general/sc_icon_loading.png', + width: 121.w, + fit: BoxFit.fitWidth, + ), + ); + list.add(SizedBox(height: height(10.w))); + list.add( + Text( + msg, + style: TextStyle( + fontSize: sp(12), + color: textColor, + fontWeight: FontWeight.w400, + decoration: TextDecoration.none, + height: 1, + ), + ), + ); + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: + center ? MainAxisAlignment.center : MainAxisAlignment.start, + children: list, + ), + ], + ); +} + +msgRoleTag(String role, {double? width, double? height}) { + String image = ""; + if (SCRoomRolesType.HOMEOWNER.name == role) { + image = 'fz'; + } else if (SCRoomRolesType.ADMIN.name == role) { + image = "gly"; + } else if (SCRoomRolesType.MEMBER.name == role) { + image = "hy"; + } else if (SCRoomRolesType.TOURIST.name == role) { + image = "guest"; + } + if (image.isEmpty) return SizedBox.shrink(); + return Container( + alignment: Alignment.center, + width: width ?? 22.w, + height: height ?? 22.w, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage("sc_images/room/sc_icon_room_$image.png"), + ), + ), + ); +} + +///性别 0女 1男 +xb(num? sex, {double? height, Color? color = Colors.white}) { + // print('sex:$sex'); + // if (sex == null) return Container(); + String image = "sc_images/login/sc_icon_sex_man.png"; + if (sex == 0) { + image = "sc_images/login/sc_icon_sex_woman.png"; + } + return Image.asset( + image, + height: height ?? 14.w, + fit: BoxFit.fill, + color: color, + ); +} + +xb2(num? sex, {double? width, double? height}) { + // print('sex:$sex'); + if (sex == null) return Container(); + String image = "sc_images/login/sc_icon_sex_man_bg.png"; + if (sex == 0) { + image = "sc_images/login/sc_icon_sex_woman_bg.png"; + } + return Image.asset( + image, + width: width ?? 28.w, + height: height ?? 14.w, + fit: BoxFit.fill, + ); +} + +///搜索框 +searchWidget({ + Function? onChange, + Function? onTap, + String? hint, + double? hintSize, + Color? borderColor, + EdgeInsetsGeometry? padding, + TextEditingController? controller, + Color? textColor, + Color? serachIconColor, + FocusNode? focusNode, +}) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (onTap != null) { + onTap(); + } + }, + child: Container( + margin: + padding ?? + EdgeInsetsDirectional.only(start: width(15), end: width(0)), + alignment: AlignmentDirectional.centerStart, + height: width(36), + //width: width(345), + decoration: BoxDecoration( + color: Color(0xff18F2B1).withOpacity(0.1), + borderRadius: BorderRadius.all(Radius.circular(height(20))), + border: Border.all(color: borderColor ?? Colors.black12, width: 1.w), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(width: width(10)), + Image.asset( + "sc_images/index/sc_icon_serach2.png", + width: 20.w, + color: serachIconColor ?? Color(0xff18F2B1), + height: 20.w, + ), + SizedBox(width: width(6)), + _buildPhoneInput( + onChange, + onTap == null, + hint ?? 'Search by ID', + controller, + textColor, + hintSize, + focusNode, + ), + ], + ), + ), + ); +} + +_buildPhoneInput( + Function? onChange, + bool enabled, + String tint, + TextEditingController? controller, + Color? textColor, + double? hintSize, + FocusNode? focusNode, +) { + return Expanded( + child: TextField( + controller: controller, + onChanged: (text) { + if (onChange != null) { + onChange(text); + } + }, + focusNode: focusNode, + autofocus: false, + enabled: enabled, + maxLength: 11, + cursorColor: SocialChatTheme.primaryColor, + decoration: InputDecoration( + hintText: tint, + hintStyle: TextStyle( + color: SocialChatTheme.textSecondary, + fontSize: hintSize ?? sp(14), + ), + counterText: '', + isDense: true, + filled: false, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + focusedErrorBorder: InputBorder.none, + contentPadding: EdgeInsets.only(bottom: 0), + ), + style: TextStyle( + textBaseline: TextBaseline.alphabetic, + fontSize: hintSize ?? sp(14), + color: textColor ?? Colors.black, + ), + ), + ); +} + +///vip标识 +getVIPBadge(String? type, {double? width, double? height}) { + return Container(); +} + +///用户等级 +getUserLevel(num level, {double? width, double? height, double? fontSize}) { + String icon = ""; + if (level > 0 && level < 11) { + icon = "sc_images/level/sc_icon_user_level_1_10.png"; + } else if (level > 10 && level < 21) { + icon = "sc_images/level/sc_icon_user_level_10_20.png"; + } else if (level > 20 && level < 31) { + icon = "sc_images/level/sc_icon_user_level_20_30.png"; + } else if (level > 30 && level < 41) { + icon = "sc_images/level/sc_icon_user_level_30_40.png"; + } else if (level > 40 && level < 51) { + icon = "sc_images/level/sc_icon_user_level_40_50.png"; + } + return icon.isNotEmpty + ? Directionality( + textDirection: TextDirection.ltr, + child: Stack( + alignment: AlignmentDirectional.center, + children: [ + Image.asset(icon, width: width, height: height, fit: BoxFit.fill), + PositionedDirectional( + end: level > 9 ? 10.w : 15.w, + child: text( + "$level", + fontSize: fontSize ?? 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ) + : Container(); +} + +///用户魅力等级 +getWealthLevel(num level, {double? width, double? height, double? fontSize}) { + String icon = ""; + if (level > 0 && level < 11) { + icon = "sc_images/level/sc_icon_wealth_level_1_10.png"; + } else if (level > 10 && level < 21) { + icon = "sc_images/level/sc_icon_wealth_level_10_20.png"; + } else if (level > 20 && level < 31) { + icon = "sc_images/level/sc_icon_wealth_level_20_30.png"; + } else if (level > 30 && level < 41) { + icon = "sc_images/level/sc_icon_wealth_level_30_40.png"; + } else if (level > 40 && level < 51) { + icon = "sc_images/level/sc_icon_wealth_level_40_50.png"; + } + return icon.isNotEmpty + ? Directionality( + textDirection: TextDirection.ltr, + child: Stack( + alignment: AlignmentDirectional.center, + children: [ + Image.asset(icon, width: width, height: height, fit: BoxFit.fill), + PositionedDirectional( + end: level > 9 ? 10.w : 15.w, + child: text( + "$level", + fontSize: fontSize ?? 12.sp, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ) + : Container(); +} + +String getGiftObtCoinsBg(num obtCoins) { + String icon = ""; + if (obtCoins > -1 && obtCoins < 5000) { + icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_0.png"; + } else if (obtCoins > 4999 && obtCoins < 50000) { + icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_1.png"; + } else if (obtCoins > 49999 && obtCoins < 100000) { + icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_2.png"; + } else if (obtCoins > 99999 && obtCoins < 1000000) { + icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_3.png"; + } else if (obtCoins > 999999 && obtCoins < 10000000) { + icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_4.png"; + } else if (obtCoins > 9999999) { + icon = "sc_images/room/sc_icon_luck_gift_obt_coins_bg_5.png"; + } + return icon; +} + +///数字图片 +buildNum(String num, {double? size = 18}) { + if (num.isNotEmpty) { + List eList = []; + int index = 0; + num.split("").forEach((item) { + if (item == "k") { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.9).w, + ), + child: Image.asset( + "sc_images/room/sc_icon_k.png", + width: size, + height: size, + ), + ), + ); + } else if (item == "m") { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.9).w, + ), + child: Image.asset( + "sc_images/room/sc_icon_m.png", + width: size, + height: size, + ), + ), + ); + } else { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.8).w, + ), + child: Image.asset( + "sc_images/room/sc_icon_number_$item.png", + width: size, + height: size, + ), + ), + ); + } + index = index + 1; + }); + return Directionality( + textDirection: TextDirection.ltr, + child: Stack(children: eList), + ); + } else { + return Container(); + } +} + +///游戏飘屏数字图片 +buildNumForGame(String num, {double? size = 18}) { + if (num.isNotEmpty) { + List eList = []; + int index = 0; + num.split("").forEach((item) { + if (item == "k") { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.65).w, + ), + child: Image.asset( + "sc_images/general/sc_icon_game_numk.png", + width: size, + height: size, + ), + ), + ); + } else if (item == "m") { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.65).w, + ), + child: Image.asset( + "sc_images/general/sc_icon_game_numm.png", + width: size, + height: size, + ), + ), + ); + } else { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.6).w, + ), + child: Image.asset( + "sc_images/general/sc_icon_game_num$item.png", + width: size, + height: size, + ), + ), + ); + } + index = index + 1; + }); + return Directionality( + textDirection: TextDirection.ltr, + child: Stack(children: eList), + ); + } else { + return Container(); + } +} + +///房间幸运数字 +buildNumForRoomLuckNum(String num, {double? size = 18}) { + if (num.isNotEmpty) { + List eList = []; + int index = 0; + num.split("").forEach((item) { + eList.add( + Container( + margin: EdgeInsetsDirectional.only( + start: (index * (size ?? 18) * 0.7).w, + ), + child: Image.asset( + "sc_images/room/sc_icon_lucknumber_$item.png", + width: size, + height: size, + ), + ), + ); + index = index + 1; + }); + return Directionality( + textDirection: TextDirection.ltr, + child: Stack(children: eList), + ); + } else { + return Container(); + } +} + +buildFamilyProgressLinearGradient(int level) { + if (level == 0) { + return LinearGradient(colors: [Color(0xff2E4957), Color(0xffC9E7E8)]); + } else if (level == 1) { + return LinearGradient(colors: [Color(0xff5A3F31), Color(0xffF8CD85)]); + } else if (level == 2) { + return LinearGradient(colors: [Color(0xff0D5ACE), Color(0xffCBD9E8)]); + } else if (level == 3) { + return LinearGradient(colors: [Color(0xffC57E1C), Color(0xffCBD9E8)]); + } else if (level == 10) { + return LinearGradient(colors: [Color(0xff3C7FBE), Color(0xffADE1F1)]); + } else if (level == 11) { + return LinearGradient(colors: [Color(0xff300A67), Color(0xffA53EFF)]); + } + return LinearGradient(colors: [Color(0xff2E4957), Color(0xffC9E7E8)]); +} + +buildVipNameColor(String type) { + return null; +} diff --git a/lib/ui_kit/widgets/room/room_msg_item.dart b/lib/ui_kit/widgets/room/room_msg_item.dart index 58e3d52..f57fe05 100644 --- a/lib/ui_kit/widgets/room/room_msg_item.dart +++ b/lib/ui_kit/widgets/room/room_msg_item.dart @@ -127,41 +127,38 @@ class _MsgItemState extends State { Flexible( // 使用Flexible替代Expanded child: GestureDetector( - child:Container( - constraints: BoxConstraints( - maxWidth: - ScreenUtil().screenWidth * 0.7, // 最大宽度约束 - ), - decoration: BoxDecoration( - color: Colors.black38, - borderRadius: BorderRadius.only( - topRight: Radius.circular(15.w), - bottomLeft: Radius.circular(15.w), - bottomRight: Radius.circular(15.w), - ), - ), - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 4.w, - ), - margin: EdgeInsetsDirectional.only( - start: 54.w, - ).copyWith(bottom: 8.w), - child: ExtendedText( - widget.msg?.msg ?? '', - style: TextStyle( - fontSize: sp(13), - color: Color( - _msgColor(widget.msg.type ?? ""), - ), - fontWeight: FontWeight.w500, - ), - softWrap: true, // 自动换行 - specialTextSpanBuilder: AtTextSpanBuilder( - onTapCall: (userId) {}, - ), - ), - ), + child: Container( + constraints: BoxConstraints( + maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束 + ), + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15.w), + bottomLeft: Radius.circular(15.w), + bottomRight: Radius.circular(15.w), + ), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 4.w, + ), + margin: EdgeInsetsDirectional.only( + start: 54.w, + ).copyWith(bottom: 8.w), + child: ExtendedText( + widget.msg?.msg ?? '', + style: TextStyle( + fontSize: sp(13), + color: Color(_msgColor(widget.msg.type ?? "")), + fontWeight: FontWeight.w500, + ), + softWrap: true, // 自动换行 + specialTextSpanBuilder: AtTextSpanBuilder( + onTapCall: (userId) {}, + ), + ), + ), onLongPress: () { if (!(widget.msg?.msg ?? "").startsWith(AtText.flag)) { Clipboard.setData( @@ -568,68 +565,66 @@ class _MsgItemState extends State { children: [ Flexible( // 使用Flexible替代Expanded - child:Container( - constraints: BoxConstraints( - maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束 - ), - decoration: BoxDecoration( - color: Colors.black38, - borderRadius: BorderRadius.only( - topRight: Radius.circular(15.w), - bottomLeft: Radius.circular(15.w), - bottomRight: Radius.circular(15.w), - ), - ), - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 4.w, - ), - margin: EdgeInsetsDirectional.only( - start: 54.w, - ).copyWith(bottom: 8.w), - child: Text.rich( - TextSpan( - children: [ - TextSpan( - text: SCAppLocalizations.of(context)!.sendTo, - style: TextStyle( - fontSize: 13.sp, - color: Colors.white, - ), - ), - TextSpan(text: " "), - TextSpan( - text: widget.msg.toUser?.userNickname ?? "", - style: TextStyle( - fontSize: 13.sp, - color: SocialChatTheme.primaryColor, - ), - recognizer: - TapGestureRecognizer()..onTap = () {}, - ), - TextSpan(text: " "), - WidgetSpan( - child: netImage( - url: widget.msg.gift?.giftPhoto ?? "", - width: 22.w, - height: 22.w, - ), - ), - TextSpan(text: " "), - TextSpan( - text: "x${widget.msg.number}", - style: TextStyle( - fontSize: 13.sp, - color: Colors.white, - ), - recognizer: - TapGestureRecognizer()..onTap = () {}, - ), - ], - ), - textAlign: TextAlign.left, + child: Container( + constraints: BoxConstraints( + maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束 + ), + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15.w), + bottomLeft: Radius.circular(15.w), + bottomRight: Radius.circular(15.w), + ), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 4.w, + ), + margin: EdgeInsetsDirectional.only( + start: 54.w, + ).copyWith(bottom: 8.w), + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: SCAppLocalizations.of(context)!.sendTo, + style: TextStyle( + fontSize: 13.sp, + color: Colors.white, ), ), + TextSpan(text: " "), + TextSpan( + text: widget.msg.toUser?.userNickname ?? "", + style: TextStyle( + fontSize: 13.sp, + color: SocialChatTheme.primaryColor, + ), + recognizer: TapGestureRecognizer()..onTap = () {}, + ), + TextSpan(text: " "), + WidgetSpan( + child: netImage( + url: widget.msg.gift?.giftPhoto ?? "", + width: 22.w, + height: 22.w, + ), + ), + TextSpan(text: " "), + TextSpan( + text: "x${widget.msg.number}", + style: TextStyle( + fontSize: 13.sp, + color: Colors.white, + ), + recognizer: TapGestureRecognizer()..onTap = () {}, + ), + ], + ), + textAlign: TextAlign.left, + ), + ), ), ], ), @@ -829,28 +824,27 @@ class _MsgItemState extends State { Flexible( // 使用Flexible替代Expanded child: GestureDetector( - child:Container( - constraints: BoxConstraints( - maxWidth: - ScreenUtil().screenWidth * 0.5, // 最大宽度约束 - ), - decoration: BoxDecoration( - color: Colors.black38, - borderRadius: BorderRadius.only( - topRight: Radius.circular(15.w), - bottomLeft: Radius.circular(15.w), - bottomRight: Radius.circular(15.w), - ), - ), - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 4.w, - ), - margin: EdgeInsetsDirectional.only( - start: 54.w, - ).copyWith(bottom: 8.w), - child: netImage(url: widget.msg.msg ?? ""), - ), + child: Container( + constraints: BoxConstraints( + maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束 + ), + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15.w), + bottomLeft: Radius.circular(15.w), + bottomRight: Radius.circular(15.w), + ), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 4.w, + ), + margin: EdgeInsetsDirectional.only( + start: 54.w, + ).copyWith(bottom: 8.w), + child: netImage(url: widget.msg.msg ?? ""), + ), onTap: () { String encodedUrls = Uri.encodeComponent( jsonEncode([widget.msg.msg]), @@ -890,59 +884,57 @@ class _MsgItemState extends State { Flexible( // 使用Flexible替代Expanded child: GestureDetector( - child:Container( - constraints: BoxConstraints( - maxWidth: - ScreenUtil().screenWidth * 0.5, // 最大宽度约束 - ), - decoration: BoxDecoration( - color: Colors.black38, - borderRadius: BorderRadius.only( - topRight: Radius.circular(15.w), - bottomLeft: Radius.circular(15.w), - bottomRight: Radius.circular(15.w), + child: Container( + constraints: BoxConstraints( + maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束 + ), + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15.w), + bottomLeft: Radius.circular(15.w), + bottomRight: Radius.circular(15.w), + ), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 4.w, + ), + margin: EdgeInsetsDirectional.only( + start: 54.w, + ).copyWith(bottom: 8.w), + child: + (widget.msg.hasPlayedAnimation ?? false) + ? Image.asset( + "sc_images/room/sc_icon_dice_${widget.msg.msg}.png", + height: 35.w, + ) + : FutureBuilder( + future: Future.delayed( + Duration(milliseconds: 2000), ), + // 传入Future + builder: ( + BuildContext context, + AsyncSnapshot snapshot, + ) { + // 根据snapshot的状态来构建UI + if (snapshot.connectionState == + ConnectionState.done) { + widget.msg.hasPlayedAnimation = true; + return Image.asset( + "sc_images/room/sc_icon_dice_${widget.msg.msg}.png", + height: 35.w, + ); + } else { + return Image.asset( + "sc_images/room/sc_icon_dice_animl.webp", + height: 35.w, + ); + } + }, ), - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 4.w, - ), - margin: EdgeInsetsDirectional.only( - start: 54.w, - ).copyWith(bottom: 8.w), - child: - (widget.msg.hasPlayedAnimation ?? false) - ? Image.asset( - "sc_images/room/sc_icon_dice_${widget.msg.msg}.png", - height: 35.w, - ) - : FutureBuilder( - future: Future.delayed( - Duration(milliseconds: 2000), - ), - // 传入Future - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - // 根据snapshot的状态来构建UI - if (snapshot.connectionState == - ConnectionState.done) { - widget.msg.hasPlayedAnimation = - true; - return Image.asset( - "sc_images/room/sc_icon_dice_${widget.msg.msg}.png", - height: 35.w, - ); - } else { - return Image.asset( - "sc_images/room/sc_icon_dice_animl.webp", - height: 35.w, - ); - } - }, - ), - ), + ), onTap: () {}, ), ), @@ -974,59 +966,57 @@ class _MsgItemState extends State { Flexible( // 使用Flexible替代Expanded child: GestureDetector( - child:Container( - constraints: BoxConstraints( - maxWidth: - ScreenUtil().screenWidth * 0.5, // 最大宽度约束 - ), - decoration: BoxDecoration( - color: Colors.black38, - borderRadius: BorderRadius.only( - topRight: Radius.circular(15.w), - bottomLeft: Radius.circular(15.w), - bottomRight: Radius.circular(15.w), + child: Container( + constraints: BoxConstraints( + maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束 + ), + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15.w), + bottomLeft: Radius.circular(15.w), + bottomRight: Radius.circular(15.w), + ), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 4.w, + ), + margin: EdgeInsetsDirectional.only( + start: 54.w, + ).copyWith(bottom: 8.w), + child: + (widget.msg.hasPlayedAnimation ?? false) + ? Image.asset( + "sc_images/room/sc_icon_rps_${widget.msg.msg}.png", + height: 35.w, + ) + : FutureBuilder( + future: Future.delayed( + Duration(milliseconds: 2000), ), + // 传入Future + builder: ( + BuildContext context, + AsyncSnapshot snapshot, + ) { + // 根据snapshot的状态来构建UI + if (snapshot.connectionState == + ConnectionState.done) { + widget.msg.hasPlayedAnimation = true; + return Image.asset( + "sc_images/room/sc_icon_rps_${widget.msg.msg}.png", + height: 35.w, + ); + } else { + return Image.asset( + "sc_images/room/sc_icon_rps_animal.webp", + height: 35.w, + ); + } + }, ), - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 4.w, - ), - margin: EdgeInsetsDirectional.only( - start: 54.w, - ).copyWith(bottom: 8.w), - child: - (widget.msg.hasPlayedAnimation ?? false) - ? Image.asset( - "sc_images/room/sc_icon_rps_${widget.msg.msg}.png", - height: 35.w, - ) - : FutureBuilder( - future: Future.delayed( - Duration(milliseconds: 2000), - ), - // 传入Future - builder: ( - BuildContext context, - AsyncSnapshot snapshot, - ) { - // 根据snapshot的状态来构建UI - if (snapshot.connectionState == - ConnectionState.done) { - widget.msg.hasPlayedAnimation = - true; - return Image.asset( - "sc_images/room/sc_icon_rps_${widget.msg.msg}.png", - height: 35.w, - ); - } else { - return Image.asset( - "sc_images/room/sc_icon_rps_animal.webp", - height: 35.w, - ); - } - }, - ), - ), + ), onTap: () {}, ), ), @@ -1058,45 +1048,44 @@ class _MsgItemState extends State { Flexible( // 使用Flexible替代Expanded child: GestureDetector( - child:Container( - constraints: BoxConstraints( - maxWidth: - ScreenUtil().screenWidth * 0.7, // 最大宽度约束 + child: Container( + constraints: BoxConstraints( + maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束 + ), + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.only( + topRight: Radius.circular(15.w), + bottomLeft: Radius.circular(15.w), + bottomRight: Radius.circular(15.w), + ), + ), + padding: EdgeInsets.symmetric( + horizontal: 10.w, + vertical: 4.w, + ), + margin: EdgeInsetsDirectional.only( + start: 54.w, + ).copyWith(bottom: 8.w), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SCGlobalConfig.lang == "ar" + ? Image.asset( + "sc_images/room/sc_icon_luck_num_text_ar.png", + height: 20.w, + ) + : Image.asset( + "sc_images/room/sc_icon_luck_num_text_en.png", + height: 20.w, ), - decoration: BoxDecoration( - color:Colors.black38, - borderRadius: BorderRadius.only( - topRight: Radius.circular(15.w), - bottomLeft: Radius.circular(15.w), - bottomRight: Radius.circular(15.w), - ), - ), - padding: EdgeInsets.symmetric( - horizontal: 10.w, - vertical: 4.w, - ), - margin: EdgeInsetsDirectional.only( - start: 54.w, - ).copyWith(bottom: 8.w), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SCGlobalConfig.lang == "ar" - ? Image.asset( - "sc_images/room/sc_icon_luck_num_text_ar.png", - height: 20.w, - ) - : Image.asset( - "sc_images/room/sc_icon_luck_num_text_en.png", - height: 20.w, - ), - buildNumForRoomLuckNum( - widget.msg.msg ?? "", - size: 20.w, - ), - ], - ), - ), + buildNumForRoomLuckNum( + widget.msg.msg ?? "", + size: 20.w, + ), + ], + ), + ), onTap: () {}, ), ), @@ -1110,7 +1099,7 @@ class _MsgItemState extends State { _buildUserInfoLine(BuildContext context) { if ((widget.msg.needUpDataUserInfo) ?? false) { - SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""] == null; + SCRoomUtils.roomUsersMap.remove(widget.msg.user?.id ?? ""); } return SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""] == null ? FutureBuilder( @@ -1349,11 +1338,17 @@ class Msg { needUpDataUserInfo = json['needUpDataUserInfo']; userWheat = json['userWheat'] != null ? MicRes.fromJson(json['userWheat']) : null; - user = json['user'] != null ? SocialChatUserProfile.fromJson(json['user']) : null; + user = + json['user'] != null + ? SocialChatUserProfile.fromJson(json['user']) + : null; toUser = - json['toUser'] != null ? SocialChatUserProfile.fromJson(json['toUser']) : null; + json['toUser'] != null + ? SocialChatUserProfile.fromJson(json['toUser']) + : null; - gift = json['gift'] != null ? SocialChatGiftRes.fromJson(json['gift']) : null; + gift = + json['gift'] != null ? SocialChatGiftRes.fromJson(json['gift']) : null; } Map toJson() { diff --git a/需求进度.md b/需求进度.md index 4a008fe..719a341 100644 --- a/需求进度.md +++ b/需求进度.md @@ -19,6 +19,13 @@ - 已修复个人主页首屏黑屏与内容区多余圆角问题:为个人页补充稳定的底图/底色占位,避免进入页面时先出现黑屏;同时移除资料内容区顶部多余圆角。 - 已将个人主页首屏占位升级为骨架屏:进入个人页时会先按真实版式展示头像、昵称、计数区、Tab 和资料内容的骨架块,不再先露默认背景等待约 1 秒。 - 已优化语言房顶部成员入口的点击范围:右上角在线成员区域现已将左侧头像堆叠区与右侧成员图标统一为同一点击热区,用户点击头像区也可直接打开成员列表页。 +- 已继续修复房间封面跨用户不同步问题:当列表或进房返回空封面时,客户端会自动补拉 `room/profile/specific` 详情并缓存真实封面;同时房主保存房间资料后会向房间内其他用户派发资料刷新消息,避免只有自己能看到新封面、其他用户仍显示 `no data`。 +- 已修复个人主页编辑页头像上传后无变化的问题:上传成功后会先本地回写新头像地址并立即提交;资料更新接口现同时兼容 `userAvatar/avatar` 字段,且不再被紧接着的旧资料回拉覆盖,返回个人主页后头像会即时更新。 +- 已进一步定位图片 `no data` 根因并修复客户端加载链:日志确认房间封面详情接口可返回正确公网 `media...` 地址,但用户头像接口会返回 `jvapi.../external/oss/local/...` 这类受保护资源地址;共享图片组件现已对这类地址自动补齐鉴权请求头,避免其它用户侧加载时直接失败为 `no data`。 +- 已直接校验头像异常地址的服务端返回:`jvapi.../external/oss/local/...` 当前会返回 `500 local disk file does not exist`,并非单纯鉴权缺失;前端现已在编辑资料成功后与本人资料回拉时优先保留刚上传成功的公网头像,避免再次被这类坏地址覆盖,同时修复了房间消息用户资料缓存未真正失效的笔误。 +- 已继续修正个人资料更新接口的提交结构:排查发现注册接口使用嵌套 `profile`,而资料更新接口此前只发扁平字段;现已改为同时提交 `id + 扁平字段 + profile`,并在 `profile` 内同步带上 `userAvatar/avatar`,用于兼容后端可能按嵌套对象取值的情况。 +- 已按最新联调结果再次收窄个人资料更新参数:日志已证明后端能收到请求但会忽略新头像地址,当前已改为头像相关只提交单一 `userAvatar` 字段,并移除 `avatar/profile` 冗余结构,便于继续验证后端是否对字段名或重复参数敏感。 +- 已继续将个人页头像上传链路与通用资料提交拆开:上传成功后当前会单独触发一次仅含 `userAvatar` 的更新请求,不再混带昵称、性别、年龄、国家等字段,便于排除其它参数对头像更新接口的干扰。 - 完成仓库结构、依赖引用、资源体积和 APK 组成的第一轮排查。 - 移除 4 个当前未在业务层直接使用的插件依赖:`loading_indicator_view_plus`、`social_sharing_plus`、`flutter_foreground_task`、`on_audio_query`,并清理相关平台声明。 - 修补 `image_cropper 5.0.1` Android 兼容问题,切换到本地 path 依赖以恢复构建。