fix: update room rocket launch flow
This commit is contained in:
parent
ccb5579a85
commit
e5fdb78fd6
@ -21,6 +21,7 @@ import 'package:yumi/ui_kit/components/sc_tts.dart';
|
|||||||
import 'package:yumi/app/routes/sc_routes.dart';
|
import 'package:yumi/app/routes/sc_routes.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
import 'package:yumi/shared/tools/sc_loading_manager.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_pick_utils.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_gift_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_gift_repository_imp.dart';
|
||||||
@ -60,6 +61,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
bool isBlacklistLoading = true;
|
bool isBlacklistLoading = true;
|
||||||
bool isBlacklist = false;
|
bool isBlacklist = false;
|
||||||
|
bool _isBackgroundUpdating = false;
|
||||||
|
|
||||||
Map<String, SCUserCounterRes> counterMap = {};
|
Map<String, SCUserCounterRes> counterMap = {};
|
||||||
List<SocialChatGiftRes> giftWallList = [];
|
List<SocialChatGiftRes> giftWallList = [];
|
||||||
@ -582,22 +584,25 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
),
|
),
|
||||||
items:
|
items:
|
||||||
backgroundPhotos.map((item) {
|
backgroundPhotos.map((item) {
|
||||||
return GestureDetector(
|
return _buildProfileBackgroundTapTarget(
|
||||||
child: netImage(
|
ref,
|
||||||
|
netImage(
|
||||||
url: item.url ?? "",
|
url: item.url ?? "",
|
||||||
width: ScreenUtil().screenWidth,
|
width: ScreenUtil().screenWidth,
|
||||||
height: 300.w,
|
height: 300.w,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
onTap: () {},
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
)
|
)
|
||||||
: Image.asset(
|
: _buildProfileBackgroundTapTarget(
|
||||||
'sc_images/person/sc_icon_profile_card_default_bg.png',
|
ref,
|
||||||
width: ScreenUtil().screenWidth,
|
Image.asset(
|
||||||
height: 300.w,
|
'sc_images/person/sc_icon_profile_card_default_bg.png',
|
||||||
fit: BoxFit.cover,
|
width: ScreenUtil().screenWidth,
|
||||||
|
height: 300.w,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -972,6 +977,79 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildProfileBackgroundTapTarget(
|
||||||
|
SocialChatUserProfileManager ref,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap:
|
||||||
|
widget.isMe == "true"
|
||||||
|
? () => _pickAndUpdateProfileBackground(ref)
|
||||||
|
: null,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pickAndUpdateProfileBackground(SocialChatUserProfileManager ref) {
|
||||||
|
if (_isBackgroundUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SCPickUtils.pickImage(
|
||||||
|
context,
|
||||||
|
(bool success, String url) {
|
||||||
|
final imageUrl = url.trim();
|
||||||
|
if (!success || imageUrl.isEmpty || !mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_updateProfileBackground(ref, imageUrl);
|
||||||
|
},
|
||||||
|
aspectRatio: ScreenUtil().screenWidth / 300.w,
|
||||||
|
backOriginalFile: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateProfileBackground(
|
||||||
|
SocialChatUserProfileManager ref,
|
||||||
|
String imageUrl,
|
||||||
|
) async {
|
||||||
|
if (_isBackgroundUpdating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isBackgroundUpdating = true;
|
||||||
|
SCLoadingManager.show(context: context);
|
||||||
|
try {
|
||||||
|
final updatedProfile = await SCAccountRepository().updateUserInfo(
|
||||||
|
backgroundPhotos: [imageUrl],
|
||||||
|
);
|
||||||
|
final returnedPhotos = updatedProfile.backgroundPhotos ?? [];
|
||||||
|
final hasUploadedPhoto = returnedPhotos.any(
|
||||||
|
(photo) => (photo.url ?? "").trim() == imageUrl,
|
||||||
|
);
|
||||||
|
final backgroundPhotos =
|
||||||
|
hasUploadedPhoto
|
||||||
|
? returnedPhotos
|
||||||
|
: [PersonPhoto(url: imageUrl, status: 1)];
|
||||||
|
final mergedProfile = updatedProfile.copyWith(
|
||||||
|
backgroundPhotos: backgroundPhotos,
|
||||||
|
);
|
||||||
|
ref.userProfile = mergedProfile;
|
||||||
|
ref.syncCurrentUserProfile(mergedProfile);
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
SCTts.show(SCAppLocalizations.of(context)!.operationSuccessful);
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
SCTts.show("update fail $e");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_isBackgroundUpdating = false;
|
||||||
|
SCLoadingManager.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _openChat() async {
|
Future<void> _openChat() async {
|
||||||
final conversation = V2TimConversation(
|
final conversation = V2TimConversation(
|
||||||
type: ConversationType.V2TIM_C2C,
|
type: ConversationType.V2TIM_C2C,
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/sc_user_badge_res.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.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/text/sc_text.dart';
|
||||||
|
|
||||||
class ProfileWallOfHonorsPage extends StatefulWidget {
|
class ProfileWallOfHonorsPage extends StatefulWidget {
|
||||||
@ -18,11 +23,55 @@ class _ProfileWallOfHonorsPageState extends State<ProfileWallOfHonorsPage> {
|
|||||||
static const Color _mutedText = Color(0xff8F9A98);
|
static const Color _mutedText = Color(0xff8F9A98);
|
||||||
|
|
||||||
int _selectedTabIndex = 0;
|
int _selectedTabIndex = 0;
|
||||||
final int _honorCount = 0;
|
bool _loading = true;
|
||||||
final int _eventCount = 0;
|
SCUserBadgeRes _badges = SCUserBadgeRes.empty();
|
||||||
|
|
||||||
|
List<WearBadge> get _honorBadges =>
|
||||||
|
_badges.displayBadges.where(_isHonorBadge).toList();
|
||||||
|
|
||||||
|
List<WearBadge> get _eventBadges =>
|
||||||
|
_badges.displayBadges.where(_isEventBadge).toList();
|
||||||
|
|
||||||
|
int get _honorCount => _honorBadges.length;
|
||||||
|
int get _eventCount => _eventBadges.length;
|
||||||
int get _totalCount => _honorCount + _eventCount;
|
int get _totalCount => _honorCount + _eventCount;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadBadges();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadBadges() async {
|
||||||
|
final userId = widget.tageId.trim();
|
||||||
|
if (userId.isEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_loading = false;
|
||||||
|
_badges = SCUserBadgeRes.empty();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final currentUserId =
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
||||||
|
final result =
|
||||||
|
currentUserId.isNotEmpty && currentUserId == userId
|
||||||
|
? await SCAccountRepository().currentUserBadges()
|
||||||
|
: await SCAccountRepository().otherUserBadges(userId);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_badges = result;
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
|
} catch (_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() {
|
||||||
|
_badges = SCUserBadgeRes.empty(userId: userId);
|
||||||
|
_loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildTopBar() {
|
Widget _buildTopBar() {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@ -131,6 +180,106 @@ class _ProfileWallOfHonorsPageState extends State<ProfileWallOfHonorsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildLoadingState() {
|
||||||
|
return const SliverFillRemaining(
|
||||||
|
hasScrollBody: false,
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBadgeGrid() {
|
||||||
|
final list = _selectedTabIndex == 0 ? _honorBadges : _eventBadges;
|
||||||
|
if (list.isEmpty) {
|
||||||
|
return _buildEmptyState();
|
||||||
|
}
|
||||||
|
return SliverPadding(
|
||||||
|
padding: EdgeInsets.fromLTRB(16.w, 4.w, 16.w, 32.w),
|
||||||
|
sliver: SliverGrid(
|
||||||
|
delegate: SliverChildBuilderDelegate(
|
||||||
|
(context, index) => _buildBadgeItem(list[index]),
|
||||||
|
childCount: list.length,
|
||||||
|
),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
mainAxisExtent: 132.w,
|
||||||
|
mainAxisSpacing: 14.w,
|
||||||
|
crossAxisSpacing: 12.w,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBadgeItem(WearBadge badge) {
|
||||||
|
final url = _badgeUrl(badge);
|
||||||
|
final name = (badge.badgeName ?? "").trim();
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 86.w,
|
||||||
|
height: 86.w,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xff0E332D),
|
||||||
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xff18F2B1).withValues(alpha: 0.24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: netImage(
|
||||||
|
url: url,
|
||||||
|
width: 68.w,
|
||||||
|
height: 68.w,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
noDefaultImg: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8.w),
|
||||||
|
text(
|
||||||
|
name.isEmpty ? "--" : name,
|
||||||
|
fontSize: 12,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
maxLines: 2,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isHonorBadge(WearBadge badge) {
|
||||||
|
final sourceType = badge.sourceType?.trim().toUpperCase() ?? "";
|
||||||
|
final type = badge.type?.trim().toUpperCase() ?? "";
|
||||||
|
return sourceType == "ACHIEVEMENT" ||
|
||||||
|
sourceType == "VIP" ||
|
||||||
|
(sourceType.isEmpty &&
|
||||||
|
(type == "LONG" || type == "ACHIEVEMENT" || type == "VIP"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isEventBadge(WearBadge badge) {
|
||||||
|
final sourceType = badge.sourceType?.trim().toUpperCase() ?? "";
|
||||||
|
final type = badge.type?.trim().toUpperCase() ?? "";
|
||||||
|
if (sourceType == "ACTIVITY") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (sourceType.isNotEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return type == "ACTIVITY" ||
|
||||||
|
type == "SHORT" ||
|
||||||
|
(type != "LONG" && type != "VIP" && type != "ACHIEVEMENT");
|
||||||
|
}
|
||||||
|
|
||||||
|
String _badgeUrl(WearBadge badge) {
|
||||||
|
return (badge.selectUrl ?? "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -143,7 +292,7 @@ class _ProfileWallOfHonorsPageState extends State<ProfileWallOfHonorsPage> {
|
|||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Column(children: [_buildTopBar(), _buildTabs()]),
|
child: Column(children: [_buildTopBar(), _buildTabs()]),
|
||||||
),
|
),
|
||||||
_buildEmptyState(),
|
_loading ? _buildLoadingState() : _buildBadgeGrid(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -42,6 +42,8 @@ class RoomRtcAudioMixingConfig {
|
|||||||
abstract class RoomRtcEngineAdapter {
|
abstract class RoomRtcEngineAdapter {
|
||||||
RoomRtcEngineKind get kind;
|
RoomRtcEngineKind get kind;
|
||||||
|
|
||||||
|
bool get isInRoom;
|
||||||
|
|
||||||
Future<void> prewarm();
|
Future<void> prewarm();
|
||||||
|
|
||||||
Future<void> enterRoomAsAudience(RoomRtcEnterRoomConfig config);
|
Future<void> enterRoomAsAudience(RoomRtcEnterRoomConfig config);
|
||||||
|
|||||||
@ -345,7 +345,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
Timer? _roomRocketLaunchAnimationTimer;
|
Timer? _roomRocketLaunchAnimationTimer;
|
||||||
Timer? _roomRocketGiftRefreshTimer;
|
Timer? _roomRocketGiftRefreshTimer;
|
||||||
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
|
final List<Timer> _roomRocketPostLaunchRefreshTimers = [];
|
||||||
String? _lastRoomRocketLaunchNoticeKey;
|
|
||||||
Timer? _roomRedPacketPresenceTimer;
|
Timer? _roomRedPacketPresenceTimer;
|
||||||
Future<void>? _rtcEnginePrewarmTask;
|
Future<void>? _rtcEnginePrewarmTask;
|
||||||
Future<void>? _pendingRoomSwitchRtcLeaveTask;
|
Future<void>? _pendingRoomSwitchRtcLeaveTask;
|
||||||
@ -1061,6 +1060,11 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_resetLocalAudioRuntimeTracking();
|
_resetLocalAudioRuntimeTracking();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get _hasCurrentRoomRtcConnection {
|
||||||
|
final adapter = _roomRtcEngineAdapter;
|
||||||
|
return _roomRtcJoined && adapter != null && adapter.isInRoom;
|
||||||
|
}
|
||||||
|
|
||||||
bool get _canReportMicActive {
|
bool get _canReportMicActive {
|
||||||
return _roomRtcJoined && _roomRtcRoleIsBroadcaster && isOnMai();
|
return _roomRtcJoined && _roomRtcRoleIsBroadcaster && isOnMai();
|
||||||
}
|
}
|
||||||
@ -1507,6 +1511,42 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _rejoinCurrentRoomRtcForSameRoomReuse(String roomId) async {
|
||||||
|
if (_hasCurrentRoomRtcConnection) {
|
||||||
|
_setRoomStartupReady();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final trtcUserSig = await _fetchRoomRtcJoinCredentialForCurrentRoom();
|
||||||
|
if (!_isCurrentRoomId(roomId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await joinRoomRtcVoiceChannel(
|
||||||
|
throwOnError: true,
|
||||||
|
trtcUserSig: trtcUserSig,
|
||||||
|
);
|
||||||
|
if (!_isCurrentRoomId(roomId)) {
|
||||||
|
await _cleanupRoomRtcState(
|
||||||
|
clearMicSeats: currenRoom == null,
|
||||||
|
onlyIfBusinessRoomId: roomId,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await _bootstrapRoomHeartbeatState();
|
||||||
|
if (!_isCurrentRoomId(roomId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_setRoomStartupReady();
|
||||||
|
} catch (error) {
|
||||||
|
if (!_isCurrentRoomId(roomId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_stopVoiceRoomForegroundService();
|
||||||
|
_setRoomStartupFailed(RoomStartupFailureType.rtc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> _switchRoomRtcToAudience() async {
|
Future<bool> _switchRoomRtcToAudience() async {
|
||||||
try {
|
try {
|
||||||
final switched = await _currentRoomRtcEngineAdapter.switchToAudience();
|
final switched = await _currentRoomRtcEngineAdapter.switchToAudience();
|
||||||
@ -1779,12 +1819,20 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
retrieveMicrophoneList();
|
retrieveMicrophoneList();
|
||||||
fetchOnlineUsersList();
|
fetchOnlineUsersList();
|
||||||
_startRoomStatePolling();
|
_startRoomStatePolling();
|
||||||
_setRoomStartupReady();
|
final shouldRejoinRoomRtc = !_hasCurrentRoomRtcConnection;
|
||||||
|
if (shouldRejoinRoomRtc) {
|
||||||
|
_setRoomStartupLoading();
|
||||||
|
} else {
|
||||||
|
_setRoomStartupReady();
|
||||||
|
}
|
||||||
setRoomVisualEffectsEnabled(true);
|
setRoomVisualEffectsEnabled(true);
|
||||||
_startRoomRedPacketPresenceHeartbeat(roomId);
|
_startRoomRedPacketPresenceHeartbeat(roomId);
|
||||||
_refreshRoomRedPacketListAfterEntry(roomId: roomId);
|
_refreshRoomRedPacketListAfterEntry(roomId: roomId);
|
||||||
_startVoiceRoomForegroundService();
|
_startVoiceRoomForegroundService();
|
||||||
VoiceRoomRoute.openVoiceRoom(context);
|
VoiceRoomRoute.openVoiceRoom(context);
|
||||||
|
if (shouldRejoinRoomRtc) {
|
||||||
|
unawaited(_rejoinCurrentRoomRtcForSameRoomReuse(roomId));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
JoinRoomRes? preEnteredRoom;
|
JoinRoomRes? preEnteredRoom;
|
||||||
if (pwd == null && _previewRoomRequiresPassword(context, previewData)) {
|
if (pwd == null && _previewRoomRequiresPassword(context, previewData)) {
|
||||||
@ -2823,10 +2871,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
///更新房间火箭信息
|
///更新房间火箭信息
|
||||||
void updateRoomRocketConfigurationStatus(SCRoomRocketStatusRes res) {
|
void updateRoomRocketConfigurationStatus(SCRoomRocketStatusRes res) {
|
||||||
final previousStatus = roomRocketStatus;
|
|
||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_publishRoomRocketLaunchNoticeIfNeeded(previousStatus, res);
|
|
||||||
_scheduleRoomRocketAssetPreload(
|
_scheduleRoomRocketAssetPreload(
|
||||||
(res.roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim(),
|
(res.roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "").trim(),
|
||||||
res,
|
res,
|
||||||
@ -2845,10 +2891,8 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) {
|
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final previousStatus = roomRocketStatus;
|
|
||||||
roomRocketStatus = res;
|
roomRocketStatus = res;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
_publishRoomRocketLaunchNoticeIfNeeded(previousStatus, res);
|
|
||||||
_scheduleRoomRocketAssetPreload(resolvedRoomId, res);
|
_scheduleRoomRocketAssetPreload(resolvedRoomId, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2966,114 +3010,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _publishRoomRocketLaunchNoticeIfNeeded(
|
|
||||||
SCRoomRocketStatusRes? previous,
|
|
||||||
SCRoomRocketStatusRes current,
|
|
||||||
) {
|
|
||||||
if (!_shouldPublishRoomRocketLaunchNotice(previous, current)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final roomId =
|
|
||||||
(current.roomId ?? currenRoom?.roomProfile?.roomProfile?.id ?? "")
|
|
||||||
.trim();
|
|
||||||
final groupId =
|
|
||||||
(currenRoom?.roomProfile?.roomProfile?.roomAccount ?? "").trim();
|
|
||||||
if (roomId.isEmpty || groupId.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final level = _roomRocketNoticeLevel(current);
|
|
||||||
final noticeKey = _roomRocketLaunchNoticeKey(
|
|
||||||
roomId: roomId,
|
|
||||||
level: level,
|
|
||||||
roundNo: current.roundNo ?? 0,
|
|
||||||
);
|
|
||||||
if (_lastRoomRocketLaunchNoticeKey == noticeKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_lastRoomRocketLaunchNoticeKey = noticeKey;
|
|
||||||
rtmProvider?.addMsg(
|
|
||||||
Msg(
|
|
||||||
groupId: groupId,
|
|
||||||
msg: _roomRocketLaunchNoticeSeconds(current).toString(),
|
|
||||||
type: SCRoomMsgType.rocketLaunchNotice,
|
|
||||||
number: _roomRocketLaunchNoticeSeconds(current),
|
|
||||||
rocketIconUrl: _rocketIconUrlForStatus(current, level),
|
|
||||||
giftBatchId: noticeKey,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _shouldPublishRoomRocketLaunchNotice(
|
|
||||||
SCRoomRocketStatusRes? previous,
|
|
||||||
SCRoomRocketStatusRes current,
|
|
||||||
) {
|
|
||||||
if (previous == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final currentRoomId =
|
|
||||||
(currenRoom?.roomProfile?.roomProfile?.id ?? "").trim();
|
|
||||||
if (currentRoomId.isNotEmpty &&
|
|
||||||
(current.roomId ?? currentRoomId).trim() != currentRoomId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return _roomRocketStatusPercent(previous) < 99.5 &&
|
|
||||||
_roomRocketStatusPercent(current) >= 99.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
double _roomRocketStatusPercent(SCRoomRocketStatusRes? status) {
|
|
||||||
if (status == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final value = status.displayPercent ?? status.energyPercent;
|
|
||||||
if (value != null) {
|
|
||||||
final raw = value.toDouble();
|
|
||||||
final percent = raw > 0 && raw <= 1 ? raw * 100 : raw;
|
|
||||||
return percent.clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
final currentEnergy = status.currentEnergy ?? 0;
|
|
||||||
final maxEnergy = status.maxEnergy ?? status.needEnergy ?? 0;
|
|
||||||
if (maxEnergy <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ((currentEnergy / maxEnergy) * 100).clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _roomRocketNoticeLevel(SCRoomRocketStatusRes status) {
|
|
||||||
final level = status.currentLevel ?? status.level?.toInt() ?? 1;
|
|
||||||
return level.clamp(1, 99).toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _roomRocketLaunchNoticeSeconds(SCRoomRocketStatusRes status) {
|
|
||||||
final seconds = status.resetRemainingSeconds ?? 0;
|
|
||||||
if (seconds > 0 && seconds <= 60) {
|
|
||||||
return seconds;
|
|
||||||
}
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _rocketIconUrlForStatus(SCRoomRocketStatusRes status, int level) {
|
|
||||||
for (final item in status.levels ?? const []) {
|
|
||||||
if (item.level == level && item.rocketIconUrl.trim().isNotEmpty) {
|
|
||||||
return item.rocketIconUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final item in roomRocketStatus?.levels ?? const []) {
|
|
||||||
if (item.level == level && item.rocketIconUrl.trim().isNotEmpty) {
|
|
||||||
return item.rocketIconUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
String _roomRocketLaunchNoticeKey({
|
|
||||||
required String roomId,
|
|
||||||
required int level,
|
|
||||||
required int roundNo,
|
|
||||||
}) {
|
|
||||||
final roundKey = roundNo > 0 ? roundNo.toString() : 'full';
|
|
||||||
return ['rocket_launch_notice', roomId, level, roundKey].join('|');
|
|
||||||
}
|
|
||||||
|
|
||||||
void _playRoomRocketLaunchAnimation({
|
void _playRoomRocketLaunchAnimation({
|
||||||
required String animationUrl,
|
required String animationUrl,
|
||||||
required RoomRocketLaunchBroadcastMessage launch,
|
required RoomRocketLaunchBroadcastMessage launch,
|
||||||
@ -3680,7 +3616,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
_roomRocketGiftRefreshTimer?.cancel();
|
_roomRocketGiftRefreshTimer?.cancel();
|
||||||
_roomRocketGiftRefreshTimer = null;
|
_roomRocketGiftRefreshTimer = null;
|
||||||
_cancelRoomRocketPostLaunchStatusRefresh();
|
_cancelRoomRocketPostLaunchStatusRefresh();
|
||||||
_lastRoomRocketLaunchNoticeKey = null;
|
|
||||||
RoomRocketAssetPreloader.cancelCurrentRoom();
|
RoomRocketAssetPreloader.cancelCurrentRoom();
|
||||||
_stopRoomRedPacketPresenceHeartbeat();
|
_stopRoomRedPacketPresenceHeartbeat();
|
||||||
_previewRoomSeatCount = null;
|
_previewRoomSeatCount = null;
|
||||||
|
|||||||
@ -121,6 +121,7 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
static const int _roomRocketLaunchRecentTtlMs = 90000;
|
static const int _roomRocketLaunchRecentTtlMs = 90000;
|
||||||
static const int _roomRocketLaunchLooseRecentTtlMs = 12000;
|
static const int _roomRocketLaunchLooseRecentTtlMs = 12000;
|
||||||
static const int _roomRocketRegionBroadcastRecentTtlMs = 90000;
|
static const int _roomRocketRegionBroadcastRecentTtlMs = 90000;
|
||||||
|
static const Duration _createRoomGroupTimeout = Duration(seconds: 12);
|
||||||
static const List<Duration> _roomRocketRewardPopupRetryDelays = [
|
static const List<Duration> _roomRocketRewardPopupRetryDelays = [
|
||||||
Duration(milliseconds: 700),
|
Duration(milliseconds: 700),
|
||||||
Duration(seconds: 2),
|
Duration(seconds: 2),
|
||||||
@ -654,11 +655,20 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
String groupID,
|
String groupID,
|
||||||
String groupName,
|
String groupName,
|
||||||
) {
|
) {
|
||||||
return V2TIMGroupManager().createGroup(
|
return V2TIMGroupManager()
|
||||||
groupID: groupID,
|
.createGroup(
|
||||||
groupType: GroupType.AVChatRoom,
|
groupID: groupID,
|
||||||
groupName: groupName,
|
groupType: GroupType.AVChatRoom,
|
||||||
);
|
groupName: groupName,
|
||||||
|
)
|
||||||
|
.timeout(
|
||||||
|
_createRoomGroupTimeout,
|
||||||
|
onTimeout:
|
||||||
|
() => V2TimValueCallback<String>(
|
||||||
|
code: -10001,
|
||||||
|
desc: 'Create IM group timeout',
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///加入房间的im群聊
|
///加入房间的im群聊
|
||||||
@ -1931,28 +1941,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
currentContext,
|
currentContext,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
final isCurrentVisibleRoom =
|
|
||||||
rtcProvider.shouldShowRoomVisualEffects &&
|
|
||||||
rtcProvider.isVoiceRoomRouteVisible;
|
|
||||||
final launch = _buildRoomRocketLaunchFromStatusPayload(
|
|
||||||
rtcProvider: rtcProvider,
|
|
||||||
payload: data,
|
|
||||||
roomId: roomId,
|
|
||||||
);
|
|
||||||
final shouldHandleLaunch = _shouldHandleRoomRocketLaunchFromStatusPayload(
|
|
||||||
rtcProvider: rtcProvider,
|
|
||||||
payload: data,
|
|
||||||
launch: launch,
|
|
||||||
);
|
|
||||||
if (shouldHandleLaunch) {
|
|
||||||
_publishRoomRocketRegionBroadcastIfNeeded(launch);
|
|
||||||
if (isCurrentVisibleRoom) {
|
|
||||||
_handleCurrentVisibleRoomRocketLaunch(
|
|
||||||
rtcProvider: rtcProvider,
|
|
||||||
launch: launch,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unawaited(rtcProvider.refreshRoomRocketStatus(roomId: roomId));
|
unawaited(rtcProvider.refreshRoomRocketStatus(roomId: roomId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2330,141 +2318,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return ['rocket_launch_loose', launch.roomId, launch.safeLevel].join('|');
|
return ['rocket_launch_loose', launch.roomId, launch.safeLevel].join('|');
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _shouldHandleRoomRocketLaunchFromStatusPayload({
|
|
||||||
required RealTimeCommunicationManager rtcProvider,
|
|
||||||
required Map<String, dynamic> payload,
|
|
||||||
required RoomRocketLaunchBroadcastMessage launch,
|
|
||||||
}) {
|
|
||||||
final currentPercent = _roomRocketPayloadPercent(payload);
|
|
||||||
if (currentPercent < 99.5) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final previousPercent = _roomRocketStatusPercent(
|
|
||||||
rtcProvider.roomRocketStatus,
|
|
||||||
);
|
|
||||||
if (previousPercent < 99.5) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
final launchKey = _roomRocketLaunchNoticeKey(launch);
|
|
||||||
if (launchKey.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_pruneRecentRoomRocketLaunches(DateTime.now().millisecondsSinceEpoch);
|
|
||||||
final looseLaunchKey = _roomRocketLaunchLooseKey(launch);
|
|
||||||
return !_recentRoomRocketLaunchTimes.containsKey(launchKey) &&
|
|
||||||
(looseLaunchKey.isEmpty ||
|
|
||||||
!_recentRoomRocketLaunchLooseTimes.containsKey(looseLaunchKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomRocketLaunchBroadcastMessage _buildRoomRocketLaunchFromStatusPayload({
|
|
||||||
required RealTimeCommunicationManager rtcProvider,
|
|
||||||
required Map<String, dynamic> payload,
|
|
||||||
required String roomId,
|
|
||||||
}) {
|
|
||||||
final roomProfile = rtcProvider.currenRoom?.roomProfile?.roomProfile;
|
|
||||||
final level = _roomRocketPayloadLevel(
|
|
||||||
payload,
|
|
||||||
rtcProvider.roomRocketStatus?.currentLevel ??
|
|
||||||
rtcProvider.roomRocketStatus?.level?.toInt() ??
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
return RoomRocketLaunchBroadcastMessage(
|
|
||||||
roomId: roomId,
|
|
||||||
roomAccount: _firstNonBlank([
|
|
||||||
roomProfile?.roomAccount,
|
|
||||||
_payloadText(payload['roomAccount'] ?? payload['room_account']),
|
|
||||||
]),
|
|
||||||
roomName: _firstNonBlank([
|
|
||||||
roomProfile?.roomName,
|
|
||||||
_payloadText(payload['roomName'] ?? payload['room_name']),
|
|
||||||
]),
|
|
||||||
roomCoverUrl: _firstNonBlank([
|
|
||||||
roomProfile?.roomCover,
|
|
||||||
_payloadAssetText(payload['roomCoverUrl'] ?? payload['room_cover_url']),
|
|
||||||
_payloadAssetText(payload['roomCover'] ?? payload['room_cover']),
|
|
||||||
_payloadAssetText(payload['roomAvatar'] ?? payload['room_avatar']),
|
|
||||||
_payloadAssetText(payload['roomIcon'] ?? payload['room_icon']),
|
|
||||||
_payloadAssetText(payload['coverUrl'] ?? payload['cover_url']),
|
|
||||||
_payloadAssetText(payload['cover']),
|
|
||||||
_payloadAssetText(_broadcastPayloadMap(payload['room'])['coverUrl']),
|
|
||||||
_payloadAssetText(_broadcastPayloadMap(payload['room'])['cover']),
|
|
||||||
_payloadAssetText(_broadcastPayloadMap(payload['room'])['avatar']),
|
|
||||||
_payloadAssetText(
|
|
||||||
_broadcastPayloadMap(payload['roomInfo'])['coverUrl'],
|
|
||||||
),
|
|
||||||
_payloadAssetText(_broadcastPayloadMap(payload['roomInfo'])['cover']),
|
|
||||||
_payloadAssetText(_broadcastPayloadMap(payload['roomInfo'])['avatar']),
|
|
||||||
]),
|
|
||||||
roundNo:
|
|
||||||
_payloadNum(payload['roundNo'] ?? payload['round_no'])?.toInt() ?? 0,
|
|
||||||
level: level,
|
|
||||||
rocketIconUrl: _firstNonBlank([
|
|
||||||
_rocketIconFromPayload(payload),
|
|
||||||
_rocketIconUrlForLevel(level),
|
|
||||||
]),
|
|
||||||
rocketAnimationUrl: _rocketAnimationFromPayload(payload),
|
|
||||||
durationSeconds: _roomRocketPayloadNoticeSeconds(payload),
|
|
||||||
triggerUser: const RoomRocketLaunchTriggerUser(
|
|
||||||
userId: '',
|
|
||||||
nickname: '',
|
|
||||||
avatar: '',
|
|
||||||
account: '',
|
|
||||||
countryCode: '',
|
|
||||||
countryName: '',
|
|
||||||
),
|
|
||||||
triggerUserId: _payloadText(
|
|
||||||
payload['triggerUserId'] ?? payload['trigger_user_id'],
|
|
||||||
),
|
|
||||||
launchNo: _payloadText(payload['launchNo'] ?? payload['launch_no']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
double _roomRocketStatusPercent(dynamic status) {
|
|
||||||
if (status == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final value = status.displayPercent ?? status.energyPercent;
|
|
||||||
if (value is num) {
|
|
||||||
final raw = value.toDouble();
|
|
||||||
final percent = raw > 0 && raw <= 1 ? raw * 100 : raw;
|
|
||||||
return percent.clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
final currentEnergy = status.currentEnergy;
|
|
||||||
final maxEnergy = status.maxEnergy ?? status.needEnergy;
|
|
||||||
if (currentEnergy is! num || maxEnergy is! num || maxEnergy <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ((currentEnergy / maxEnergy) * 100).clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
double _roomRocketPayloadPercent(Map<String, dynamic> payload) {
|
|
||||||
final value = _payloadNum(
|
|
||||||
payload['displayPercent'] ??
|
|
||||||
payload['display_percent'] ??
|
|
||||||
payload['energyPercent'] ??
|
|
||||||
payload['energy_percent'] ??
|
|
||||||
payload['percent'],
|
|
||||||
);
|
|
||||||
if (value != null) {
|
|
||||||
final raw = value.toDouble();
|
|
||||||
final percent = raw > 0 && raw <= 1 ? raw * 100 : raw;
|
|
||||||
return percent.clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
final currentEnergy = _payloadNum(
|
|
||||||
payload['currentEnergy'] ?? payload['current_energy'],
|
|
||||||
);
|
|
||||||
final maxEnergy = _payloadNum(
|
|
||||||
payload['maxEnergy'] ??
|
|
||||||
payload['max_energy'] ??
|
|
||||||
payload['needEnergy'] ??
|
|
||||||
payload['need_energy'],
|
|
||||||
);
|
|
||||||
if (currentEnergy == null || maxEnergy == null || maxEnergy <= 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return ((currentEnergy / maxEnergy) * 100).clamp(0, 100).toDouble();
|
|
||||||
}
|
|
||||||
|
|
||||||
int _roomRocketPayloadLevel(Map<String, dynamic> payload, int fallback) {
|
int _roomRocketPayloadLevel(Map<String, dynamic> payload, int fallback) {
|
||||||
final value = _payloadNum(
|
final value = _payloadNum(
|
||||||
payload['currentLevel'] ??
|
payload['currentLevel'] ??
|
||||||
@ -2478,25 +2331,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return (value?.toInt() ?? fallback).clamp(1, 99).toInt();
|
return (value?.toInt() ?? fallback).clamp(1, 99).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
int _roomRocketPayloadNoticeSeconds(Map<String, dynamic> payload) {
|
|
||||||
final seconds =
|
|
||||||
_payloadNum(
|
|
||||||
payload['countdownSeconds'] ??
|
|
||||||
payload['countdown_seconds'] ??
|
|
||||||
payload['launchDelaySeconds'] ??
|
|
||||||
payload['launch_delay_seconds'] ??
|
|
||||||
payload['delaySeconds'] ??
|
|
||||||
payload['delay_seconds'] ??
|
|
||||||
payload['duration_seconds'] ??
|
|
||||||
payload['durationSeconds'],
|
|
||||||
)?.toInt() ??
|
|
||||||
10;
|
|
||||||
if (seconds > 0 && seconds <= 60) {
|
|
||||||
return seconds;
|
|
||||||
}
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _rocketIconFromPayload(Map<String, dynamic> payload) {
|
String _rocketIconFromPayload(Map<String, dynamic> payload) {
|
||||||
return _firstNonBlank([
|
return _firstNonBlank([
|
||||||
_payloadAssetText(payload['rocketIconUrl']),
|
_payloadAssetText(payload['rocketIconUrl']),
|
||||||
@ -2508,22 +2342,6 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _rocketAnimationFromPayload(Map<String, dynamic> payload) {
|
|
||||||
return _firstNonBlank([
|
|
||||||
_payloadAssetText(payload['rocketAnimationUrl']),
|
|
||||||
_payloadAssetText(payload['rocket_animation_url']),
|
|
||||||
_payloadAssetText(payload['rocketSvgaUrl']),
|
|
||||||
_payloadAssetText(payload['rocket_svga_url']),
|
|
||||||
_payloadAssetText(payload['animationUrl']),
|
|
||||||
_payloadAssetText(payload['animation_url']),
|
|
||||||
_payloadAssetText(payload['launchAnimationUrl']),
|
|
||||||
_payloadAssetText(payload['launch_animation_url']),
|
|
||||||
_payloadAssetText(payload['svgaUrl']),
|
|
||||||
_payloadAssetText(payload['sourceUrl']),
|
|
||||||
_payloadAssetText(payload['source_url']),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _payloadRoomId(Map<String, dynamic> payload) {
|
String _payloadRoomId(Map<String, dynamic> payload) {
|
||||||
return _firstNonBlank([
|
return _firstNonBlank([
|
||||||
_payloadText(payload['roomId']),
|
_payloadText(payload['roomId']),
|
||||||
|
|||||||
@ -206,6 +206,9 @@ class TrtcRoomRtcEngineAdapter implements RoomRtcEngineAdapter {
|
|||||||
@override
|
@override
|
||||||
RoomRtcEngineKind get kind => RoomRtcEngineKind.trtc;
|
RoomRtcEngineKind get kind => RoomRtcEngineKind.trtc;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isInRoom => _isEntered;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> prewarm() async {}
|
Future<void> prewarm() async {}
|
||||||
|
|
||||||
|
|||||||
@ -78,22 +78,28 @@ class SocialChatRoomManager extends ChangeNotifier {
|
|||||||
|
|
||||||
void createNewRoom(BuildContext context, {String? customName}) async {
|
void createNewRoom(BuildContext context, {String? customName}) async {
|
||||||
SCLoadingManager.show(context: context);
|
SCLoadingManager.show(context: context);
|
||||||
// 差异化:添加自定义名称参数(暂未使用,为未来扩展预留)
|
try {
|
||||||
if (customName != null) {
|
// 差异化:添加自定义名称参数(暂未使用,为未来扩展预留)
|
||||||
// TODO: 实现自定义房间名称
|
if (customName != null) {
|
||||||
}
|
// TODO: 实现自定义房间名称
|
||||||
await SCAccountRepository().createRoom();
|
}
|
||||||
myRoom = await SCAccountRepository().myProfile();
|
await SCAccountRepository().createRoom();
|
||||||
hasLoadedMyRoom = true;
|
myRoom = await SCAccountRepository().myProfile();
|
||||||
isMyRoomLoading = false;
|
hasLoadedMyRoom = true;
|
||||||
if (!context.mounted) {
|
isMyRoomLoading = false;
|
||||||
|
if (!context.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SCTts.show(SCAppLocalizations.of(context)!.createRoomSuccsess);
|
||||||
|
} catch (e) {
|
||||||
|
isMyRoomLoading = false;
|
||||||
|
if (context.mounted) {
|
||||||
|
SCTts.show(e.toString());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
SCLoadingManager.hide();
|
SCLoadingManager.hide();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
SCTts.show(SCAppLocalizations.of(context)!.createRoomSuccsess);
|
|
||||||
notifyListeners();
|
|
||||||
SCLoadingManager.hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearContributionLevelData({bool notify = true}) {
|
void clearContributionLevelData({bool notify = true}) {
|
||||||
|
|||||||
@ -848,6 +848,7 @@ class WearBadge {
|
|||||||
String? milestone,
|
String? milestone,
|
||||||
String? notSelectUrl,
|
String? notSelectUrl,
|
||||||
String? selectUrl,
|
String? selectUrl,
|
||||||
|
String? sourceType,
|
||||||
String? type,
|
String? type,
|
||||||
String? userId,
|
String? userId,
|
||||||
bool? use,
|
bool? use,
|
||||||
@ -862,6 +863,7 @@ class WearBadge {
|
|||||||
_milestone = milestone;
|
_milestone = milestone;
|
||||||
_notSelectUrl = notSelectUrl;
|
_notSelectUrl = notSelectUrl;
|
||||||
_selectUrl = selectUrl;
|
_selectUrl = selectUrl;
|
||||||
|
_sourceType = sourceType;
|
||||||
_type = type;
|
_type = type;
|
||||||
_userId = userId;
|
_userId = userId;
|
||||||
}
|
}
|
||||||
@ -882,7 +884,10 @@ class WearBadge {
|
|||||||
_milestone = _stringValue(json['milestone']);
|
_milestone = _stringValue(json['milestone']);
|
||||||
_notSelectUrl = _stringValue(json['notSelectUrl']);
|
_notSelectUrl = _stringValue(json['notSelectUrl']);
|
||||||
_selectUrl = _stringValue(json['url'] ?? json['selectUrl']);
|
_selectUrl = _stringValue(json['url'] ?? json['selectUrl']);
|
||||||
_type = _stringValue(json['type']);
|
final rawType = _stringValue(json['type']);
|
||||||
|
_sourceType =
|
||||||
|
_stringValue(json['sourceType']) ?? _sourceTypeFromType(rawType);
|
||||||
|
_type = rawType;
|
||||||
_userId = _stringValue(json['userId']);
|
_userId = _stringValue(json['userId']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -896,6 +901,7 @@ class WearBadge {
|
|||||||
String? _milestone;
|
String? _milestone;
|
||||||
String? _notSelectUrl;
|
String? _notSelectUrl;
|
||||||
String? _selectUrl;
|
String? _selectUrl;
|
||||||
|
String? _sourceType;
|
||||||
String? _type;
|
String? _type;
|
||||||
String? _userId;
|
String? _userId;
|
||||||
|
|
||||||
@ -910,6 +916,7 @@ class WearBadge {
|
|||||||
String? milestone,
|
String? milestone,
|
||||||
String? notSelectUrl,
|
String? notSelectUrl,
|
||||||
String? selectUrl,
|
String? selectUrl,
|
||||||
|
String? sourceType,
|
||||||
String? type,
|
String? type,
|
||||||
String? userId,
|
String? userId,
|
||||||
}) => WearBadge(
|
}) => WearBadge(
|
||||||
@ -923,6 +930,7 @@ class WearBadge {
|
|||||||
milestone: milestone ?? _milestone,
|
milestone: milestone ?? _milestone,
|
||||||
notSelectUrl: notSelectUrl ?? _notSelectUrl,
|
notSelectUrl: notSelectUrl ?? _notSelectUrl,
|
||||||
selectUrl: selectUrl ?? _selectUrl,
|
selectUrl: selectUrl ?? _selectUrl,
|
||||||
|
sourceType: sourceType ?? _sourceType,
|
||||||
type: type ?? _type,
|
type: type ?? _type,
|
||||||
userId: userId ?? _userId,
|
userId: userId ?? _userId,
|
||||||
);
|
);
|
||||||
@ -947,6 +955,8 @@ class WearBadge {
|
|||||||
|
|
||||||
String? get selectUrl => _selectUrl;
|
String? get selectUrl => _selectUrl;
|
||||||
|
|
||||||
|
String? get sourceType => _sourceType;
|
||||||
|
|
||||||
String? get type => _type;
|
String? get type => _type;
|
||||||
|
|
||||||
String? get userId => _userId;
|
String? get userId => _userId;
|
||||||
@ -963,6 +973,7 @@ class WearBadge {
|
|||||||
map['milestone'] = _milestone;
|
map['milestone'] = _milestone;
|
||||||
map['notSelectUrl'] = _notSelectUrl;
|
map['notSelectUrl'] = _notSelectUrl;
|
||||||
map['selectUrl'] = _selectUrl;
|
map['selectUrl'] = _selectUrl;
|
||||||
|
map['sourceType'] = _sourceType;
|
||||||
map['type'] = _type;
|
map['type'] = _type;
|
||||||
map['userId'] = _userId;
|
map['userId'] = _userId;
|
||||||
return map;
|
return map;
|
||||||
@ -973,6 +984,16 @@ class WearBadge {
|
|||||||
return text == null || text.isEmpty ? null : text;
|
return text == null || text.isEmpty ? null : text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String? _sourceTypeFromType(String? type) {
|
||||||
|
final normalized = type?.trim().toUpperCase();
|
||||||
|
if (normalized == "VIP" ||
|
||||||
|
normalized == "ACTIVITY" ||
|
||||||
|
normalized == "ACHIEVEMENT") {
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static int? _intValue(dynamic value) {
|
static int? _intValue(dynamic value) {
|
||||||
if (value is int) {
|
if (value is int) {
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@ -71,7 +71,7 @@ class SCUserBadgeRes {
|
|||||||
|
|
||||||
static bool _isDisplayBadgePayload(Map item) {
|
static bool _isDisplayBadgePayload(Map item) {
|
||||||
final type = _stringValue(item['type'])?.toUpperCase();
|
final type = _stringValue(item['type'])?.toUpperCase();
|
||||||
if (type != null && type != "LONG" && type != "SHORT") {
|
if (type != null && !_isDisplayType(type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +84,14 @@ class SCUserBadgeRes {
|
|||||||
sourceType == "ACHIEVEMENT";
|
sourceType == "ACHIEVEMENT";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _isDisplayType(String type) {
|
||||||
|
return type == "LONG" ||
|
||||||
|
type == "SHORT" ||
|
||||||
|
type == "VIP" ||
|
||||||
|
type == "ACTIVITY" ||
|
||||||
|
type == "ACHIEVEMENT";
|
||||||
|
}
|
||||||
|
|
||||||
static String? _stringValue(dynamic value) {
|
static String? _stringValue(dynamic value) {
|
||||||
final text = value?.toString().trim();
|
final text = value?.toString().trim();
|
||||||
return text == null || text.isEmpty ? null : text;
|
return text == null || text.isEmpty ? null : text;
|
||||||
|
|||||||
@ -1174,10 +1174,10 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
///app/user/badge/current
|
///go/app/user/badge/current
|
||||||
@override
|
@override
|
||||||
Future<SCUserBadgeRes> currentUserBadges() async {
|
Future<SCUserBadgeRes> currentUserBadges() async {
|
||||||
const path = "/app/user/badge/current";
|
const path = "/go/app/user/badge/current";
|
||||||
final result = await http.get<SCUserBadgeRes>(
|
final result = await http.get<SCUserBadgeRes>(
|
||||||
path,
|
path,
|
||||||
extra: const {BaseNetworkClient.silentErrorToastKey: true},
|
extra: const {BaseNetworkClient.silentErrorToastKey: true},
|
||||||
@ -1186,11 +1186,11 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
///app/user/badge/other
|
///go/app/user/badge/other
|
||||||
@override
|
@override
|
||||||
Future<SCUserBadgeRes> otherUserBadges(String userId) async {
|
Future<SCUserBadgeRes> otherUserBadges(String userId) async {
|
||||||
final params = <String, dynamic>{"userId": userId};
|
final params = <String, dynamic>{"userId": userId};
|
||||||
const path = "/app/user/badge/other";
|
const path = "/go/app/user/badge/other";
|
||||||
final result = await http.get<SCUserBadgeRes>(
|
final result = await http.get<SCUserBadgeRes>(
|
||||||
path,
|
path,
|
||||||
queryParams: params,
|
queryParams: params,
|
||||||
|
|||||||
@ -37,7 +37,8 @@ class SCUserBadgeStrip extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
||||||
static final Map<String, Future<SCUserBadgeRes>> _badgeFutures = {};
|
static const Duration _cacheTtl = Duration(minutes: 1);
|
||||||
|
static final Map<String, _BadgeFutureCacheEntry> _badgeFutures = {};
|
||||||
|
|
||||||
Future<SCUserBadgeRes>? _future;
|
Future<SCUserBadgeRes>? _future;
|
||||||
|
|
||||||
@ -64,7 +65,16 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
AccountStorage().getCurrentUser()?.userProfile?.id?.trim() ?? "";
|
||||||
final isCurrentUser = currentUserId.isNotEmpty && userId == currentUserId;
|
final isCurrentUser = currentUserId.isNotEmpty && userId == currentUserId;
|
||||||
final cacheKey = isCurrentUser ? "current:$userId" : "other:$userId";
|
final cacheKey = isCurrentUser ? "current:$userId" : "other:$userId";
|
||||||
return _badgeFutures.putIfAbsent(cacheKey, () async {
|
final now = DateTime.now();
|
||||||
|
_badgeFutures.removeWhere(
|
||||||
|
(_, entry) => now.difference(entry.createdAt) >= _cacheTtl,
|
||||||
|
);
|
||||||
|
final cacheEntry = _badgeFutures[cacheKey];
|
||||||
|
if (cacheEntry != null &&
|
||||||
|
now.difference(cacheEntry.createdAt) < _cacheTtl) {
|
||||||
|
return cacheEntry.future;
|
||||||
|
}
|
||||||
|
final future = () async {
|
||||||
try {
|
try {
|
||||||
return isCurrentUser
|
return isCurrentUser
|
||||||
? await SCAccountRepository().currentUserBadges()
|
? await SCAccountRepository().currentUserBadges()
|
||||||
@ -73,7 +83,9 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
_badgeFutures.remove(cacheKey);
|
_badgeFutures.remove(cacheKey);
|
||||||
return SCUserBadgeRes.empty(userId: userId);
|
return SCUserBadgeRes.empty(userId: userId);
|
||||||
}
|
}
|
||||||
});
|
}();
|
||||||
|
_badgeFutures[cacheKey] = _BadgeFutureCacheEntry(future, now);
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get _normalizedUserId => widget.userId?.trim() ?? "";
|
String get _normalizedUserId => widget.userId?.trim() ?? "";
|
||||||
@ -146,7 +158,8 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
|
|
||||||
Widget _buildBadge(WearBadge badge, {required double badgeHeight}) {
|
Widget _buildBadge(WearBadge badge, {required double badgeHeight}) {
|
||||||
final type = badge.type?.trim().toUpperCase() ?? "";
|
final type = badge.type?.trim().toUpperCase() ?? "";
|
||||||
final isLongBadge = type == "LONG";
|
final sourceType = badge.sourceType?.trim().toUpperCase() ?? "";
|
||||||
|
final isLongBadge = type == "LONG" || type == "VIP" || sourceType == "VIP";
|
||||||
final width =
|
final width =
|
||||||
isLongBadge
|
isLongBadge
|
||||||
? (widget.longBadgeWidth ?? badgeHeight * 2.2)
|
? (widget.longBadgeWidth ?? badgeHeight * 2.2)
|
||||||
@ -180,3 +193,10 @@ class _SCUserBadgeStripState extends State<SCUserBadgeStrip> {
|
|||||||
return spaced;
|
return spaced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _BadgeFutureCacheEntry {
|
||||||
|
const _BadgeFutureCacheEntry(this.future, this.createdAt);
|
||||||
|
|
||||||
|
final Future<SCUserBadgeRes> future;
|
||||||
|
final DateTime createdAt;
|
||||||
|
}
|
||||||
|
|||||||
@ -1242,23 +1242,66 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildUserNameAndLevels(SocialChatUserProfile? user) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 23.w,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
fit: FlexFit.loose,
|
||||||
|
child: socialchatNickNameText(
|
||||||
|
user?.userNickname ?? "",
|
||||||
|
maxWidth: 112.w,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 13.sp,
|
||||||
|
type: user?.getVIP()?.name ?? "",
|
||||||
|
needScroll: (user?.userNickname?.characters.length ?? 0) > 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 4.w),
|
||||||
|
getWealthLevel(
|
||||||
|
user?.wealthLevel ?? 0,
|
||||||
|
width: 42.w,
|
||||||
|
height: 20.w,
|
||||||
|
fontSize: 9.sp,
|
||||||
|
),
|
||||||
|
SizedBox(width: 2.w),
|
||||||
|
getUserLevel(
|
||||||
|
user?.charmLevel ?? 0,
|
||||||
|
width: 42.w,
|
||||||
|
height: 20.w,
|
||||||
|
fontSize: 9.sp,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WearBadge> _activeWearBadges(SocialChatUserProfile? user) {
|
||||||
|
return user?.wearBadge?.where((item) => item.use ?? false).toList() ??
|
||||||
|
const <WearBadge>[];
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildMedals({
|
Widget _buildMedals({
|
||||||
required String? userId,
|
required String? userId,
|
||||||
List<WearBadge> fallbackBadges = const <WearBadge>[],
|
List<WearBadge> fallbackBadges = const <WearBadge>[],
|
||||||
}) {
|
}) {
|
||||||
final hasUserId = (userId ?? "").trim().isNotEmpty;
|
final hasUserId = (userId ?? "").trim().isNotEmpty;
|
||||||
if (!hasUserId && fallbackBadges.isEmpty) {
|
if (!hasUserId && fallbackBadges.isEmpty) {
|
||||||
return Container();
|
return SizedBox(height: 16.w);
|
||||||
}
|
}
|
||||||
return Expanded(
|
return SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
child: SCUserBadgeStrip(
|
child: SCUserBadgeStrip(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
fallbackBadges: fallbackBadges,
|
fallbackBadges: fallbackBadges,
|
||||||
height: 25.w,
|
height: 16.w,
|
||||||
badgeHeight: 25.w,
|
badgeHeight: 14.w,
|
||||||
longBadgeWidth: 55.w,
|
longBadgeWidth: 34.w,
|
||||||
spacing: 5.w,
|
spacing: 3.w,
|
||||||
maxBadges: 8,
|
maxBadges: 8,
|
||||||
|
reserveSpace: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1800,58 +1843,20 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
),
|
),
|
||||||
SizedBox(width: 3.w),
|
SizedBox(width: 3.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: SizedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
height: 48.w,
|
||||||
children: [
|
child: Column(
|
||||||
Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
getWealthLevel(
|
children: [
|
||||||
widget.msg.user?.wealthLevel ?? 0,
|
_buildUserNameAndLevels(widget.msg.user),
|
||||||
width: 48.w,
|
SizedBox(height: 2.w),
|
||||||
height: 23.w,
|
_buildMedals(
|
||||||
fontSize: 10.sp,
|
userId: widget.msg.user?.id,
|
||||||
),
|
fallbackBadges: _activeWearBadges(widget.msg.user),
|
||||||
getUserLevel(
|
),
|
||||||
widget.msg.user?.charmLevel ?? 0,
|
],
|
||||||
width: 48.w,
|
),
|
||||||
height: 23.w,
|
|
||||||
fontSize: 10.sp,
|
|
||||||
),
|
|
||||||
SizedBox(width: 3.w),
|
|
||||||
socialchatNickNameText(
|
|
||||||
maxWidth: 120.w,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
widget.msg.user?.userNickname ?? "",
|
|
||||||
fontSize: 13.sp,
|
|
||||||
type: widget.msg.user?.getVIP()?.name ?? "",
|
|
||||||
needScroll:
|
|
||||||
(widget.msg.user?.userNickname?.characters.length ??
|
|
||||||
0) >
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
widget.msg.user?.getVIP() != null
|
|
||||||
? netImage(
|
|
||||||
url: widget.msg.user?.getVIP()?.cover ?? "",
|
|
||||||
width: 25.w,
|
|
||||||
height: 25.w,
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
_buildMedals(
|
|
||||||
userId: widget.msg.user?.id,
|
|
||||||
fallbackBadges:
|
|
||||||
widget.msg.user?.wearBadge
|
|
||||||
?.where((item) => item.use ?? false)
|
|
||||||
.toList() ??
|
|
||||||
const <WearBadge>[],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -1884,56 +1889,20 @@ class _MsgItemState extends State<MsgItem> {
|
|||||||
),
|
),
|
||||||
SizedBox(width: 3.w),
|
SizedBox(width: 3.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: SizedBox(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
height: 48.w,
|
||||||
children: [
|
child: Column(
|
||||||
Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
getWealthLevel(
|
children: [
|
||||||
user.wealthLevel ?? 0,
|
_buildUserNameAndLevels(user),
|
||||||
width: 48.w,
|
SizedBox(height: 2.w),
|
||||||
height: 23.w,
|
_buildMedals(
|
||||||
fontSize: 10.sp,
|
userId: user.id,
|
||||||
),
|
fallbackBadges: _activeWearBadges(user),
|
||||||
getUserLevel(
|
),
|
||||||
user.charmLevel ?? 0,
|
],
|
||||||
width: 48.w,
|
),
|
||||||
height: 23.w,
|
|
||||||
fontSize: 10.sp,
|
|
||||||
),
|
|
||||||
SizedBox(width: 3.w),
|
|
||||||
socialchatNickNameText(
|
|
||||||
maxWidth: 120.w,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
user.userNickname ?? "",
|
|
||||||
fontSize: 13.sp,
|
|
||||||
type: user.getVIP()?.name ?? "",
|
|
||||||
needScroll:
|
|
||||||
(user.userNickname?.characters.length ?? 0) > 10,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
user.getVIP() != null
|
|
||||||
? netImage(
|
|
||||||
url: user.getVIP()?.cover ?? "",
|
|
||||||
width: 25.w,
|
|
||||||
height: 25.w,
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
_buildMedals(
|
|
||||||
userId: user.id,
|
|
||||||
fallbackBadges:
|
|
||||||
user.wearBadge
|
|
||||||
?.where((item) => item.use ?? false)
|
|
||||||
.toList() ??
|
|
||||||
const <WearBadge>[],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -23,6 +23,8 @@ import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
|||||||
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
import 'package:yumi/shared/tools/sc_room_utils.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_user_repository_impl.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/room_user_card_res.dart'
|
||||||
|
as room_card;
|
||||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
@ -34,12 +36,12 @@ import '../../../shared/business_logic/models/res/sc_user_identity_res.dart';
|
|||||||
import '../../../modules/chat/chat_route.dart';
|
import '../../../modules/chat/chat_route.dart';
|
||||||
|
|
||||||
class RoomUserInfoCard extends StatefulWidget {
|
class RoomUserInfoCard extends StatefulWidget {
|
||||||
String? userId;
|
final String? userId;
|
||||||
|
|
||||||
RoomUserInfoCard({super.key, this.userId});
|
const RoomUserInfoCard({super.key, this.userId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RoomUserInfoCardState createState() => _RoomUserInfoCardState();
|
State<RoomUserInfoCard> createState() => _RoomUserInfoCardState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
||||||
@ -180,7 +182,11 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
SizedBox(height: 6.w),
|
SizedBox(height: 6.w),
|
||||||
_buildInfoChips(context, profile),
|
_buildInfoChips(context, profile),
|
||||||
SizedBox(height: 9.w),
|
SizedBox(height: 9.w),
|
||||||
_buildBadgeStrip(profile?.id ?? widget.userId),
|
_buildBadgeStrip(
|
||||||
|
profile?.id ?? widget.userId,
|
||||||
|
ref,
|
||||||
|
profile,
|
||||||
|
),
|
||||||
SizedBox(height: 10.w),
|
SizedBox(height: 10.w),
|
||||||
_buildActions(
|
_buildActions(
|
||||||
context: context,
|
context: context,
|
||||||
@ -408,9 +414,14 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBadgeStrip(String? userId) {
|
Widget _buildBadgeStrip(
|
||||||
|
String? userId,
|
||||||
|
SocialChatUserProfileManager ref,
|
||||||
|
SocialChatUserProfile? profile,
|
||||||
|
) {
|
||||||
return SCUserBadgeStrip(
|
return SCUserBadgeStrip(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
|
fallbackBadges: _roomCardFallbackBadges(ref, profile),
|
||||||
height: 55.w,
|
height: 55.w,
|
||||||
badgeHeight: 23.w,
|
badgeHeight: 23.w,
|
||||||
longBadgeWidth: 58.w,
|
longBadgeWidth: 58.w,
|
||||||
@ -421,6 +432,46 @@ class _RoomUserInfoCardState extends State<RoomUserInfoCard> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<WearBadge> _roomCardFallbackBadges(
|
||||||
|
SocialChatUserProfileManager ref,
|
||||||
|
SocialChatUserProfile? profile,
|
||||||
|
) {
|
||||||
|
final wearBadges =
|
||||||
|
profile?.wearBadge
|
||||||
|
?.where((item) => item.use != false && _hasBadgeUrl(item))
|
||||||
|
.toList() ??
|
||||||
|
const <WearBadge>[];
|
||||||
|
if (wearBadges.isNotEmpty) {
|
||||||
|
return wearBadges;
|
||||||
|
}
|
||||||
|
return ref.userCardInfo?.useBadge
|
||||||
|
?.map(_roomUseBadgeToWearBadge)
|
||||||
|
.where(_hasBadgeUrl)
|
||||||
|
.toList() ??
|
||||||
|
const <WearBadge>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
WearBadge _roomUseBadgeToWearBadge(room_card.UseBadge badge) {
|
||||||
|
return WearBadge(
|
||||||
|
animationUrl: badge.animationUrl,
|
||||||
|
badgeKey: badge.badgeKey,
|
||||||
|
badgeLevel: badge.badgeLevel,
|
||||||
|
badgeName: badge.badgeName,
|
||||||
|
expireTime: badge.expireTime?.toInt(),
|
||||||
|
id: badge.id,
|
||||||
|
milestone: badge.milestone,
|
||||||
|
notSelectUrl: badge.notSelectUrl,
|
||||||
|
selectUrl: badge.selectUrl,
|
||||||
|
type: badge.type,
|
||||||
|
userId: badge.userId,
|
||||||
|
use: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _hasBadgeUrl(WearBadge badge) {
|
||||||
|
return (badge.selectUrl ?? "").trim().isNotEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildActions({
|
Widget _buildActions({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required SocialChatUserProfileManager ref,
|
required SocialChatUserProfileManager ref,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user