bug
This commit is contained in:
parent
a6fe7c7c48
commit
37a04d5ad7
@ -509,6 +509,24 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
|||||||
return primary ?? 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) {
|
num? _preferNonZero(num? primary, num? fallback) {
|
||||||
if (primary != null && primary != 0) {
|
if (primary != null && primary != 0) {
|
||||||
return primary;
|
return primary;
|
||||||
@ -562,9 +580,9 @@ class _SCEditProfilePageState extends State<SCEditProfilePage> {
|
|||||||
);
|
);
|
||||||
final submittedProfile = userProvider?.editUser;
|
final submittedProfile = userProvider?.editUser;
|
||||||
if (submittedProfile != null) {
|
if (submittedProfile != null) {
|
||||||
final mergedProfile =
|
final mergedProfile = (user.userProfile ?? SocialChatUserProfile())
|
||||||
(user.userProfile ?? SocialChatUserProfile()).copyWith(
|
.copyWith(
|
||||||
userAvatar: _preferNonEmpty(
|
userAvatar: _preferUsableAvatar(
|
||||||
user.userProfile?.userAvatar,
|
user.userProfile?.userAvatar,
|
||||||
submittedProfile.userAvatar,
|
submittedProfile.userAvatar,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -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/ui_kit/components/dialog/dialog_base.dart';
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.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/shared/tools/sc_pick_utils.dart';
|
||||||
import 'package:yumi/services/room/rc_room_manager.dart';
|
import 'package:yumi/services/room/rc_room_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtm_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/tools/sc_lk_dialog_util.dart';
|
||||||
import '../../../shared/data_sources/models/enum/sc_room_info_event_type.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';
|
import '../../../shared/business_logic/usecases/sc_accurate_length_limiting_textInput_formatter.dart';
|
||||||
@ -545,6 +547,21 @@ class _RoomEditPageState extends State<RoomEditPage> {
|
|||||||
roomDesc: mergedRoomInfo.roomDesc,
|
roomDesc: mergedRoomInfo.roomDesc,
|
||||||
);
|
);
|
||||||
roomManager.updateMyRoomInfo(mergedRoomInfo);
|
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") {
|
if (widget.needRestCurrentRoomInfo == "true") {
|
||||||
///需要创建群组
|
///需要创建群组
|
||||||
var c = await currentRtmProvider.createRoomGroup(
|
var c = await currentRtmProvider.createRoomGroup(
|
||||||
|
|||||||
@ -85,6 +85,31 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
super.dispose();
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return Stack(
|
||||||
@ -123,10 +148,13 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
children: [
|
children: [
|
||||||
netImage(
|
netImage(
|
||||||
url:
|
url:
|
||||||
UserManager()
|
_preferNonEmpty(
|
||||||
.getCurrentUser()
|
userCover,
|
||||||
?.userProfile
|
UserManager()
|
||||||
?.userAvatar ??
|
.getCurrentUser()
|
||||||
|
?.userProfile
|
||||||
|
?.userAvatar,
|
||||||
|
) ??
|
||||||
"",
|
"",
|
||||||
width: 45.w,
|
width: 45.w,
|
||||||
height: 45.w,
|
height: 45.w,
|
||||||
@ -143,9 +171,15 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SCPickUtils.pickImage(context, (bool success, String url) {
|
SCPickUtils.pickImage(context, (
|
||||||
|
bool success,
|
||||||
|
String url,
|
||||||
|
) {
|
||||||
if (success) {
|
if (success) {
|
||||||
submit(context);
|
debugPrint("[Profile Avatar] uploaded url: $url");
|
||||||
|
userCover = url;
|
||||||
|
setState(() {});
|
||||||
|
submitAvatarOnly(context);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -153,8 +187,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
_buildItem(
|
_buildItem(
|
||||||
"${SCAppLocalizations.of(context)!.userName}:",
|
"${SCAppLocalizations.of(context)!.userName}:",
|
||||||
nickName ??
|
nickName,
|
||||||
"",
|
|
||||||
() {
|
() {
|
||||||
_showInputBioHobby(nickName, 3);
|
_showInputBioHobby(nickName, 3);
|
||||||
},
|
},
|
||||||
@ -315,6 +348,50 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
|
|
||||||
int sTime = 0;
|
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<SocialChatUserProfileManager>(
|
||||||
|
context,
|
||||||
|
listen: false,
|
||||||
|
).syncCurrentUserProfile(mergedProfile);
|
||||||
|
Provider.of<RtcProvider>(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 {
|
void submit(BuildContext context) async {
|
||||||
if (nickName.isEmpty) {
|
if (nickName.isEmpty) {
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.pleaseEnterNickname);
|
SCTts.show(SCAppLocalizations.of(context)!.pleaseEnterNickname);
|
||||||
@ -324,6 +401,9 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
if (bTime - sTime > 5000) {
|
if (bTime - sTime > 5000) {
|
||||||
sTime = bTime;
|
sTime = bTime;
|
||||||
SCLoadingManager.show();
|
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 {
|
try {
|
||||||
final updatedProfile = await SCAccountRepository().updateUserInfo(
|
final updatedProfile = await SCAccountRepository().updateUserInfo(
|
||||||
userAvatar: userCover,
|
userAvatar: userCover,
|
||||||
@ -337,6 +417,30 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
autograph: autograph,
|
autograph: autograph,
|
||||||
countryId: country?.id,
|
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) {
|
if (!mounted) {
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
return;
|
return;
|
||||||
@ -344,19 +448,19 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
Provider.of<SocialChatUserProfileManager>(
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).syncCurrentUserProfile(updatedProfile);
|
).syncCurrentUserProfile(mergedProfile);
|
||||||
Provider.of<RtcProvider>(context, listen: false).needUpDataUserInfo =
|
Provider.of<RtcProvider>(context, listen: false).needUpDataUserInfo =
|
||||||
true;
|
true;
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful);
|
SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint(e.toString());
|
debugPrint(e.toString());
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
Provider.of<SocialChatUserProfileManager>(
|
|
||||||
context,
|
|
||||||
listen: false,
|
|
||||||
).fetchUserProfileData();
|
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,11 +555,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
alignment: AlignmentDirectional.centerEnd,
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
child: text(
|
child: text(value, textColor: Colors.white, fontSize: 15.sp),
|
||||||
value,
|
|
||||||
textColor: Colors.white,
|
|
||||||
fontSize: 15.sp,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Icon(
|
Icon(
|
||||||
@ -530,20 +630,22 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SCNavigatorUtils.push(context, CountryRoute.country, replace: false).then(
|
SCNavigatorUtils.push(
|
||||||
(res) {
|
context,
|
||||||
var c =
|
CountryRoute.country,
|
||||||
Provider.of<SCAppGeneralManager>(
|
replace: false,
|
||||||
navigatorKey.currentState!.context,
|
).then((res) {
|
||||||
listen: false,
|
var c =
|
||||||
).selectCountryInfo;
|
Provider.of<SCAppGeneralManager>(
|
||||||
if (c != null) {
|
navigatorKey.currentState!.context,
|
||||||
country = c;
|
listen: false,
|
||||||
submit(navigatorKey.currentState!.context);
|
).selectCountryInfo;
|
||||||
setState(() {});
|
if (c != null) {
|
||||||
}
|
country = c;
|
||||||
},
|
submit(navigatorKey.currentState!.context);
|
||||||
);
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -608,10 +710,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
isDense: true,
|
isDense: true,
|
||||||
hintText: "",
|
hintText: "",
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(color: Colors.white, fontSize: 14.sp),
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 14.sp,
|
|
||||||
),
|
|
||||||
contentPadding: EdgeInsets.only(top: 0.w),
|
contentPadding: EdgeInsets.only(top: 0.w),
|
||||||
counterText: '',
|
counterText: '',
|
||||||
filled: false,
|
filled: false,
|
||||||
@ -662,7 +761,7 @@ class _EditUserInfoPage2State extends State<EditUserInfoPage2>
|
|||||||
} else if (type == 2) {
|
} else if (type == 2) {
|
||||||
hobby = _inputController.text;
|
hobby = _inputController.text;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}else if (type == 3) {
|
} else if (type == 3) {
|
||||||
nickName = _inputController.text;
|
nickName = _inputController.text;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,8 @@ class SCGeneralRepositoryImp implements SocialChatGeneralRepository {
|
|||||||
filePath.lastIndexOf("/") + 1,
|
filePath.lastIndexOf("/") + 1,
|
||||||
filePath.length,
|
filePath.length,
|
||||||
);
|
);
|
||||||
|
debugPrint("[上传头像图片地址] filePath: $filePath");
|
||||||
|
debugPrint("[上传头像图片] fileName: $name");
|
||||||
FormData formData = FormData.fromMap({
|
FormData formData = FormData.fromMap({
|
||||||
"file": await MultipartFile.fromFile(filePath, filename: name),
|
"file": await MultipartFile.fromFile(filePath, filename: name),
|
||||||
});
|
});
|
||||||
@ -38,8 +40,8 @@ class SCGeneralRepositoryImp implements SocialChatGeneralRepository {
|
|||||||
response.data as Map<String, dynamic>,
|
response.data as Map<String, dynamic>,
|
||||||
fromJsonT: (json) => json as String,
|
fromJsonT: (json) => json as String,
|
||||||
);
|
);
|
||||||
debugPrint("[OSS Upload] response.data: ${response.data}");
|
debugPrint("[返回值] response.data: ${response.data}");
|
||||||
debugPrint("[OSS Upload] parsed file url: ${baseResponse.body ?? ""}");
|
debugPrint("[返回图片地址] parsed file url: ${baseResponse.body ?? ""}");
|
||||||
return baseResponse.body ?? "";
|
return baseResponse.body ?? "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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_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_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_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/shared/business_logic/models/res/user_count_guard_res.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -45,6 +44,45 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
return _instance ??= SCChatRoomRepository._internal();
|
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<SocialChatRoomRes> _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
|
///room/live-voice/discovery
|
||||||
@override
|
@override
|
||||||
Future<List<SocialChatRoomRes>> discovery({bool? allRegion}) async {
|
Future<List<SocialChatRoomRes>> discovery({bool? allRegion}) async {
|
||||||
@ -59,7 +97,7 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
(json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result.map(SCRoomProfileCache.applyToSocialChatRoomRes).toList();
|
return Future.wait(result.map(_hydrateRoomRes));
|
||||||
}
|
}
|
||||||
|
|
||||||
///live/mic/list
|
///live/mic/list
|
||||||
@ -86,6 +124,15 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
queryParams: queryParams,
|
queryParams: queryParams,
|
||||||
fromJson: (json) => MyRoomRes.fromJson(json),
|
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);
|
return SCRoomProfileCache.applyToMyRoomRes(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +375,7 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
(json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result.map(SCRoomProfileCache.applyToSocialChatRoomRes).toList();
|
return Future.wait(result.map(_hydrateRoomRes));
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/blacklist/join
|
///room/blacklist/join
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:extended_image/extended_image.dart';
|
import 'package:extended_image/extended_image.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -7,6 +9,7 @@ import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
|||||||
import 'package:tancent_vap/utils/constant.dart';
|
import 'package:tancent_vap/utils/constant.dart';
|
||||||
import 'package:tancent_vap/widgets/vap_view.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/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/ui_kit/widgets/headdress/headdress_widget.dart';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
@ -17,6 +20,37 @@ import '../../shared/data_sources/models/enum/sc_room_roles_type.dart';
|
|||||||
|
|
||||||
const String kRoomCoverDefaultImg = "sc_images/general/sc_no_data.png";
|
const String kRoomCoverDefaultImg = "sc_images/general/sc_no_data.png";
|
||||||
|
|
||||||
|
bool _requiresAuthenticatedImageHeaders(String url) {
|
||||||
|
return url.contains("/external/oss/local/");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String>? _buildImageHeaders(String url) {
|
||||||
|
if (!_requiresAuthenticatedImageHeaders(url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final uri = Uri.tryParse(url);
|
||||||
|
final headers = <String, String>{
|
||||||
|
"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) {
|
String resolveRoomCoverUrl(String? roomId, String? roomCover) {
|
||||||
final cache = SCRoomProfileCache.getRoomProfile(roomId ?? "");
|
final cache = SCRoomProfileCache.getRoomProfile(roomId ?? "");
|
||||||
return SCRoomProfileCache.preferCachedValue(cache["roomCover"], roomCover) ??
|
return SCRoomProfileCache.preferCachedValue(cache["roomCover"], roomCover) ??
|
||||||
@ -184,6 +218,7 @@ Widget netImage({
|
|||||||
url,
|
url,
|
||||||
width: width,
|
width: width,
|
||||||
height: height ?? width,
|
height: height ?? width,
|
||||||
|
headers: _buildImageHeaders(url),
|
||||||
fit: fit ?? BoxFit.cover,
|
fit: fit ?? BoxFit.cover,
|
||||||
cache: true,
|
cache: true,
|
||||||
shape: shape ?? BoxShape.rectangle,
|
shape: shape ?? BoxShape.rectangle,
|
||||||
|
|||||||
@ -127,41 +127,38 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
// 使用Flexible替代Expanded
|
// 使用Flexible替代Expanded
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child:Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth:
|
maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
||||||
ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Colors.black38,
|
||||||
color: Colors.black38,
|
borderRadius: BorderRadius.only(
|
||||||
borderRadius: BorderRadius.only(
|
topRight: Radius.circular(15.w),
|
||||||
topRight: Radius.circular(15.w),
|
bottomLeft: Radius.circular(15.w),
|
||||||
bottomLeft: Radius.circular(15.w),
|
bottomRight: Radius.circular(15.w),
|
||||||
bottomRight: Radius.circular(15.w),
|
),
|
||||||
),
|
),
|
||||||
),
|
padding: EdgeInsets.symmetric(
|
||||||
padding: EdgeInsets.symmetric(
|
horizontal: 10.w,
|
||||||
horizontal: 10.w,
|
vertical: 4.w,
|
||||||
vertical: 4.w,
|
),
|
||||||
),
|
margin: EdgeInsetsDirectional.only(
|
||||||
margin: EdgeInsetsDirectional.only(
|
start: 54.w,
|
||||||
start: 54.w,
|
).copyWith(bottom: 8.w),
|
||||||
).copyWith(bottom: 8.w),
|
child: ExtendedText(
|
||||||
child: ExtendedText(
|
widget.msg?.msg ?? '',
|
||||||
widget.msg?.msg ?? '',
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: sp(13),
|
||||||
fontSize: sp(13),
|
color: Color(_msgColor(widget.msg.type ?? "")),
|
||||||
color: Color(
|
fontWeight: FontWeight.w500,
|
||||||
_msgColor(widget.msg.type ?? ""),
|
),
|
||||||
),
|
softWrap: true, // 自动换行
|
||||||
fontWeight: FontWeight.w500,
|
specialTextSpanBuilder: AtTextSpanBuilder(
|
||||||
),
|
onTapCall: (userId) {},
|
||||||
softWrap: true, // 自动换行
|
),
|
||||||
specialTextSpanBuilder: AtTextSpanBuilder(
|
),
|
||||||
onTapCall: (userId) {},
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (!(widget.msg?.msg ?? "").startsWith(AtText.flag)) {
|
if (!(widget.msg?.msg ?? "").startsWith(AtText.flag)) {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
@ -568,68 +565,66 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
children: [
|
children: [
|
||||||
Flexible(
|
Flexible(
|
||||||
// 使用Flexible替代Expanded
|
// 使用Flexible替代Expanded
|
||||||
child:Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black38,
|
color: Colors.black38,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topRight: Radius.circular(15.w),
|
topRight: Radius.circular(15.w),
|
||||||
bottomLeft: Radius.circular(15.w),
|
bottomLeft: Radius.circular(15.w),
|
||||||
bottomRight: Radius.circular(15.w),
|
bottomRight: Radius.circular(15.w),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: 10.w,
|
horizontal: 10.w,
|
||||||
vertical: 4.w,
|
vertical: 4.w,
|
||||||
),
|
),
|
||||||
margin: EdgeInsetsDirectional.only(
|
margin: EdgeInsetsDirectional.only(
|
||||||
start: 54.w,
|
start: 54.w,
|
||||||
).copyWith(bottom: 8.w),
|
).copyWith(bottom: 8.w),
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: SCAppLocalizations.of(context)!.sendTo,
|
text: SCAppLocalizations.of(context)!.sendTo,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13.sp,
|
fontSize: 13.sp,
|
||||||
color: Colors.white,
|
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,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
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<MsgItem> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
// 使用Flexible替代Expanded
|
// 使用Flexible替代Expanded
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child:Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth:
|
maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
||||||
ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Colors.black38,
|
||||||
color: Colors.black38,
|
borderRadius: BorderRadius.only(
|
||||||
borderRadius: BorderRadius.only(
|
topRight: Radius.circular(15.w),
|
||||||
topRight: Radius.circular(15.w),
|
bottomLeft: Radius.circular(15.w),
|
||||||
bottomLeft: Radius.circular(15.w),
|
bottomRight: Radius.circular(15.w),
|
||||||
bottomRight: Radius.circular(15.w),
|
),
|
||||||
),
|
),
|
||||||
),
|
padding: EdgeInsets.symmetric(
|
||||||
padding: EdgeInsets.symmetric(
|
horizontal: 10.w,
|
||||||
horizontal: 10.w,
|
vertical: 4.w,
|
||||||
vertical: 4.w,
|
),
|
||||||
),
|
margin: EdgeInsetsDirectional.only(
|
||||||
margin: EdgeInsetsDirectional.only(
|
start: 54.w,
|
||||||
start: 54.w,
|
).copyWith(bottom: 8.w),
|
||||||
).copyWith(bottom: 8.w),
|
child: netImage(url: widget.msg.msg ?? ""),
|
||||||
child: netImage(url: widget.msg.msg ?? ""),
|
),
|
||||||
),
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
String encodedUrls = Uri.encodeComponent(
|
String encodedUrls = Uri.encodeComponent(
|
||||||
jsonEncode([widget.msg.msg]),
|
jsonEncode([widget.msg.msg]),
|
||||||
@ -890,59 +884,57 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
// 使用Flexible替代Expanded
|
// 使用Flexible替代Expanded
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child:Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth:
|
maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
||||||
ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Colors.black38,
|
||||||
color: Colors.black38,
|
borderRadius: BorderRadius.only(
|
||||||
borderRadius: BorderRadius.only(
|
topRight: Radius.circular(15.w),
|
||||||
topRight: Radius.circular(15.w),
|
bottomLeft: Radius.circular(15.w),
|
||||||
bottomLeft: Radius.circular(15.w),
|
bottomRight: 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<void>(
|
||||||
|
future: Future.delayed(
|
||||||
|
Duration(milliseconds: 2000),
|
||||||
),
|
),
|
||||||
|
// 传入Future
|
||||||
|
builder: (
|
||||||
|
BuildContext context,
|
||||||
|
AsyncSnapshot<void> 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<void>(
|
|
||||||
future: Future.delayed(
|
|
||||||
Duration(milliseconds: 2000),
|
|
||||||
),
|
|
||||||
// 传入Future
|
|
||||||
builder: (
|
|
||||||
BuildContext context,
|
|
||||||
AsyncSnapshot<void> 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: () {},
|
onTap: () {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -974,59 +966,57 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
// 使用Flexible替代Expanded
|
// 使用Flexible替代Expanded
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child:Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth:
|
maxWidth: ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
||||||
ScreenUtil().screenWidth * 0.5, // 最大宽度约束
|
),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Colors.black38,
|
||||||
color: Colors.black38,
|
borderRadius: BorderRadius.only(
|
||||||
borderRadius: BorderRadius.only(
|
topRight: Radius.circular(15.w),
|
||||||
topRight: Radius.circular(15.w),
|
bottomLeft: Radius.circular(15.w),
|
||||||
bottomLeft: Radius.circular(15.w),
|
bottomRight: 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<void>(
|
||||||
|
future: Future.delayed(
|
||||||
|
Duration(milliseconds: 2000),
|
||||||
),
|
),
|
||||||
|
// 传入Future
|
||||||
|
builder: (
|
||||||
|
BuildContext context,
|
||||||
|
AsyncSnapshot<void> 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<void>(
|
|
||||||
future: Future.delayed(
|
|
||||||
Duration(milliseconds: 2000),
|
|
||||||
),
|
|
||||||
// 传入Future
|
|
||||||
builder: (
|
|
||||||
BuildContext context,
|
|
||||||
AsyncSnapshot<void> 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: () {},
|
onTap: () {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1058,45 +1048,44 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
// 使用Flexible替代Expanded
|
// 使用Flexible替代Expanded
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child:Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth:
|
maxWidth: ScreenUtil().screenWidth * 0.7, // 最大宽度约束
|
||||||
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(
|
buildNumForRoomLuckNum(
|
||||||
color:Colors.black38,
|
widget.msg.msg ?? "",
|
||||||
borderRadius: BorderRadius.only(
|
size: 20.w,
|
||||||
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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1110,7 +1099,7 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
|
|
||||||
_buildUserInfoLine(BuildContext context) {
|
_buildUserInfoLine(BuildContext context) {
|
||||||
if ((widget.msg.needUpDataUserInfo) ?? false) {
|
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
|
return SCRoomUtils.roomUsersMap[widget.msg.user?.id ?? ""] == null
|
||||||
? FutureBuilder(
|
? FutureBuilder(
|
||||||
@ -1349,11 +1338,17 @@ class Msg {
|
|||||||
needUpDataUserInfo = json['needUpDataUserInfo'];
|
needUpDataUserInfo = json['needUpDataUserInfo'];
|
||||||
userWheat =
|
userWheat =
|
||||||
json['userWheat'] != null ? MicRes.fromJson(json['userWheat']) : null;
|
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 =
|
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<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
|||||||
7
需求进度.md
7
需求进度.md
@ -19,6 +19,13 @@
|
|||||||
- 已修复个人主页首屏黑屏与内容区多余圆角问题:为个人页补充稳定的底图/底色占位,避免进入页面时先出现黑屏;同时移除资料内容区顶部多余圆角。
|
- 已修复个人主页首屏黑屏与内容区多余圆角问题:为个人页补充稳定的底图/底色占位,避免进入页面时先出现黑屏;同时移除资料内容区顶部多余圆角。
|
||||||
- 已将个人主页首屏占位升级为骨架屏:进入个人页时会先按真实版式展示头像、昵称、计数区、Tab 和资料内容的骨架块,不再先露默认背景等待约 1 秒。
|
- 已将个人主页首屏占位升级为骨架屏:进入个人页时会先按真实版式展示头像、昵称、计数区、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 组成的第一轮排查。
|
- 完成仓库结构、依赖引用、资源体积和 APK 组成的第一轮排查。
|
||||||
- 移除 4 个当前未在业务层直接使用的插件依赖:`loading_indicator_view_plus`、`social_sharing_plus`、`flutter_foreground_task`、`on_audio_query`,并清理相关平台声明。
|
- 移除 4 个当前未在业务层直接使用的插件依赖:`loading_indicator_view_plus`、`social_sharing_plus`、`flutter_foreground_task`、`on_audio_query`,并清理相关平台声明。
|
||||||
- 修补 `image_cropper 5.0.1` Android 兼容问题,切换到本地 path 依赖以恢复构建。
|
- 修补 `image_cropper 5.0.1` Android 兼容问题,切换到本地 path 依赖以恢复构建。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user