动画 以及头像框等已知问题
This commit is contained in:
parent
905e48592e
commit
41671f9e43
@ -1,9 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_debouncer/flutter_debouncer.dart';
|
import 'package:flutter_debouncer/flutter_debouncer.dart';
|
||||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
@ -28,7 +26,6 @@ import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.d
|
|||||||
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/business_logic/models/res/mic_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/mic_res.dart';
|
||||||
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
import 'package:yumi/services/general/sc_app_general_manager.dart';
|
||||||
import 'package:yumi/services/gift/gift_system_manager.dart';
|
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||||||
@ -81,6 +78,34 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
int giftType = 0;
|
int giftType = 0;
|
||||||
Debouncer debouncer = Debouncer();
|
Debouncer debouncer = Debouncer();
|
||||||
|
|
||||||
|
void _giftFxLog(String message) {
|
||||||
|
debugPrint('[GiftFX][Send] $message');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleGiftSelected(
|
||||||
|
SocialChatGiftRes? gift, {
|
||||||
|
required int nextGiftType,
|
||||||
|
}) {
|
||||||
|
checkedGift = gift;
|
||||||
|
if (gift != null &&
|
||||||
|
(gift.giftSourceUrl ?? "").isNotEmpty &&
|
||||||
|
scGiftHasFullScreenEffect(gift.special)) {
|
||||||
|
SCGiftVapSvgaManager().preload(gift.giftSourceUrl!, highPriority: true);
|
||||||
|
_giftFxLog(
|
||||||
|
'preload selected gift '
|
||||||
|
'giftId=${gift.id} '
|
||||||
|
'giftName=${gift.giftName} '
|
||||||
|
'giftSourceUrl=${gift.giftSourceUrl} '
|
||||||
|
'special=${gift.special}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
number = 1;
|
||||||
|
noShowNumber = false;
|
||||||
|
setState(() {
|
||||||
|
giftType = nextGiftType;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -91,20 +116,15 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
Provider.of<SocialChatUserProfileManager>(context, listen: false).balance();
|
Provider.of<SocialChatUserProfileManager>(context, listen: false).balance();
|
||||||
_pages.add(
|
_pages.add(
|
||||||
GiftTabPage("ALL", (int checkedI) {
|
GiftTabPage("ALL", (int checkedI) {
|
||||||
checkedGift = null;
|
|
||||||
var all =
|
var all =
|
||||||
Provider.of<SCAppGeneralManager>(
|
Provider.of<SCAppGeneralManager>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
).giftByTab["ALL"];
|
).giftByTab["ALL"];
|
||||||
if (all != null) {
|
_handleGiftSelected(
|
||||||
checkedGift = all[checkedI];
|
all != null ? all[checkedI] : null,
|
||||||
}
|
nextGiftType: 0,
|
||||||
number = 1;
|
);
|
||||||
noShowNumber = false;
|
|
||||||
setState(() {
|
|
||||||
giftType = 0;
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -426,7 +446,8 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
horizontal: 20.w,
|
horizontal: 20.w,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: SocialChatTheme.primaryLight,
|
color:
|
||||||
|
SocialChatTheme.primaryLight,
|
||||||
borderRadius:
|
borderRadius:
|
||||||
BorderRadius.circular(5),
|
BorderRadius.circular(5),
|
||||||
),
|
),
|
||||||
@ -743,6 +764,16 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
roomId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
roomId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||||||
)
|
)
|
||||||
.then((result) {
|
.then((result) {
|
||||||
|
_giftFxLog(
|
||||||
|
'giveGift success giftId=${checkedGift?.id} '
|
||||||
|
'giftName=${checkedGift?.giftName} '
|
||||||
|
'giftSourceUrl=${checkedGift?.giftSourceUrl} '
|
||||||
|
'special=${checkedGift?.special} '
|
||||||
|
'giftTab=${checkedGift?.giftTab} '
|
||||||
|
'number=$number '
|
||||||
|
'acceptUserIds=${acceptUserIds.join(",")} '
|
||||||
|
'roomId=${rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id}',
|
||||||
|
);
|
||||||
// SCTts.show(SCAppLocalizations.of(context)!.giftGivingSuccessful);
|
// SCTts.show(SCAppLocalizations.of(context)!.giftGivingSuccessful);
|
||||||
sendGiftMsg(acceptUsers);
|
sendGiftMsg(acceptUsers);
|
||||||
Provider.of<SocialChatUserProfileManager>(
|
Provider.of<SocialChatUserProfileManager>(
|
||||||
@ -750,12 +781,38 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
listen: false,
|
listen: false,
|
||||||
).updateBalance(result);
|
).updateBalance(result);
|
||||||
})
|
})
|
||||||
.catchError((e) {});
|
.catchError((e) {
|
||||||
|
_giftFxLog(
|
||||||
|
'giveGift failed giftId=${checkedGift?.id} '
|
||||||
|
'giftName=${checkedGift?.giftName} '
|
||||||
|
'error=$e',
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendGiftMsg(List<MicRes> acceptUsers) {
|
void sendGiftMsg(List<MicRes> acceptUsers) {
|
||||||
///发送一条IM消息
|
///发送一条IM消息
|
||||||
for (var u in acceptUsers) {
|
for (var u in acceptUsers) {
|
||||||
|
final special = checkedGift?.special ?? "";
|
||||||
|
final giftSourceUrl = checkedGift?.giftSourceUrl ?? "";
|
||||||
|
final hasSource = giftSourceUrl.isNotEmpty;
|
||||||
|
final hasAnimation = scGiftHasAnimationSpecial(special);
|
||||||
|
final hasGlobalGift = special.contains(SCGiftType.GLOBAL_GIFT.name);
|
||||||
|
final hasFullScreenEffect = scGiftHasFullScreenEffect(special);
|
||||||
|
_giftFxLog(
|
||||||
|
'dispatch gift msg '
|
||||||
|
'giftId=${checkedGift?.id} '
|
||||||
|
'giftName=${checkedGift?.giftName} '
|
||||||
|
'toUserId=${u.user?.id} '
|
||||||
|
'toUserName=${u.user?.userNickname} '
|
||||||
|
'giftSourceUrl=$giftSourceUrl '
|
||||||
|
'special=$special '
|
||||||
|
'hasSource=$hasSource '
|
||||||
|
'hasAnimation=$hasAnimation '
|
||||||
|
'hasGlobalGift=$hasGlobalGift '
|
||||||
|
'hasFullScreenEffect=$hasFullScreenEffect '
|
||||||
|
'effectsEnabled=${SCGlobalConfig.isGiftSpecialEffects}',
|
||||||
|
);
|
||||||
Provider.of<RtmProvider>(
|
Provider.of<RtmProvider>(
|
||||||
navigatorKey.currentState!.context,
|
navigatorKey.currentState!.context,
|
||||||
listen: false,
|
listen: false,
|
||||||
@ -808,12 +865,35 @@ class _GiftPageState extends State<GiftPage>
|
|||||||
OverlayManager().addMessage(fMsg);
|
OverlayManager().addMessage(fMsg);
|
||||||
}
|
}
|
||||||
if (checkedGift!.giftSourceUrl != null && checkedGift!.special != null) {
|
if (checkedGift!.giftSourceUrl != null && checkedGift!.special != null) {
|
||||||
if (checkedGift!.special!.contains(SCGiftType.ANIMSCION.name) ||
|
if (scGiftHasFullScreenEffect(checkedGift!.special)) {
|
||||||
checkedGift!.special!.contains(SCGiftType.GLOBAL_GIFT.name)) {
|
|
||||||
if (SCGlobalConfig.isGiftSpecialEffects) {
|
if (SCGlobalConfig.isGiftSpecialEffects) {
|
||||||
|
_giftFxLog(
|
||||||
|
'local trigger player play '
|
||||||
|
'path=${checkedGift!.giftSourceUrl} '
|
||||||
|
'giftId=${checkedGift?.id} '
|
||||||
|
'giftName=${checkedGift?.giftName}',
|
||||||
|
);
|
||||||
SCGiftVapSvgaManager().play(checkedGift!.giftSourceUrl!);
|
SCGiftVapSvgaManager().play(checkedGift!.giftSourceUrl!);
|
||||||
|
} else {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip local play because isGiftSpecialEffects=false '
|
||||||
|
'giftId=${checkedGift?.id}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip local play because special does not include '
|
||||||
|
'${SCGiftType.ANIMSCION.name}/$kSCGiftAnimationSpecialAlias/${SCGiftType.GLOBAL_GIFT.name} '
|
||||||
|
'giftId=${checkedGift?.id} special=${checkedGift?.special}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip local play because giftSourceUrl or special is null '
|
||||||
|
'giftId=${checkedGift?.id} '
|
||||||
|
'giftSourceUrl=${checkedGift?.giftSourceUrl} '
|
||||||
|
'special=${checkedGift?.special}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ class _GiftTabPageState extends State<GiftTabPage> {
|
|||||||
)
|
)
|
||||||
: Column(
|
: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 23.w,),
|
SizedBox(height: 23.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
controller: _giftPageController,
|
controller: _giftPageController,
|
||||||
@ -250,7 +250,7 @@ class _GiftTabPageState extends State<GiftTabPage> {
|
|||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
textColor: widget.isDark ? Colors.white : Colors.black,
|
textColor: widget.isDark ? Colors.white : Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -280,23 +280,29 @@ class _GiftTabPageState extends State<GiftTabPage> {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(height: 3.w),
|
SizedBox(height: 3.w),
|
||||||
// 只添加需要的 widget
|
// 只添加需要的 widget
|
||||||
if (gift.special!.contains(SCGiftType.ANIMSCION.name))
|
if (scGiftHasFullScreenEffect(gift.special))
|
||||||
Image.asset(
|
Image.asset(
|
||||||
_strategy.getGiftPageGiftEffectIcon(SCGiftType.ANIMSCION.name),
|
_strategy.getGiftPageGiftEffectIcon(
|
||||||
|
SCGiftType.ANIMSCION.name,
|
||||||
|
),
|
||||||
width: 16.w,
|
width: 16.w,
|
||||||
height: 16.w,
|
height: 16.w,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
if (gift.special!.contains(SCGiftType.MUSIC.name))
|
if (gift.special!.contains(SCGiftType.MUSIC.name))
|
||||||
Image.asset(
|
Image.asset(
|
||||||
_strategy.getGiftPageGiftMusicIcon(SCGiftType.MUSIC.name),
|
_strategy.getGiftPageGiftMusicIcon(
|
||||||
|
SCGiftType.MUSIC.name,
|
||||||
|
),
|
||||||
width: 16.w,
|
width: 16.w,
|
||||||
height: 16.w,
|
height: 16.w,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
if (gift.giftTab == (SCGiftType.LUCKY_GIFT.name))
|
if (gift.giftTab == (SCGiftType.LUCKY_GIFT.name))
|
||||||
Image.asset(
|
Image.asset(
|
||||||
_strategy.getGiftPageGiftLuckIcon(SCGiftType.LUCKY_GIFT.name),
|
_strategy.getGiftPageGiftLuckIcon(
|
||||||
|
SCGiftType.LUCKY_GIFT.name,
|
||||||
|
),
|
||||||
width: 16.w,
|
width: 16.w,
|
||||||
height: 16.w,
|
height: 16.w,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
@ -333,7 +339,8 @@ class _GiftTabPageState extends State<GiftTabPage> {
|
|||||||
margin: EdgeInsets.symmetric(horizontal: 4.w),
|
margin: EdgeInsets.symmetric(horizontal: 4.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: _index == i ? SocialChatTheme.primaryColor : Color(0xffDADADA),
|
color:
|
||||||
|
_index == i ? SocialChatTheme.primaryColor : Color(0xffDADADA),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -63,7 +63,11 @@ class _RoomFollowPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: netImage(
|
child: netImage(
|
||||||
url: roomRes.roomProfile?.roomCover ?? "",
|
url: resolveRoomCoverUrl(
|
||||||
|
roomRes.roomProfile?.id,
|
||||||
|
roomRes.roomProfile?.roomCover,
|
||||||
|
),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
width: 200.w,
|
width: 200.w,
|
||||||
height: 200.w,
|
height: 200.w,
|
||||||
|
|||||||
@ -63,7 +63,11 @@ class _SCRoomHistoryPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: netImage(
|
child: netImage(
|
||||||
url: roomRes.roomProfile?.roomCover ?? "",
|
url: resolveRoomCoverUrl(
|
||||||
|
roomRes.roomProfile?.id,
|
||||||
|
roomRes.roomProfile?.roomCover,
|
||||||
|
),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
width: 200.w,
|
width: 200.w,
|
||||||
height: 200.w,
|
height: 200.w,
|
||||||
@ -74,7 +78,9 @@ class _SCRoomHistoryPageState
|
|||||||
margin: EdgeInsets.symmetric(horizontal: 1.w),
|
margin: EdgeInsets.symmetric(horizontal: 1.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: AssetImage("sc_images/index/sc_icon_index_room_brd.png"),
|
image: AssetImage(
|
||||||
|
"sc_images/index/sc_icon_index_room_brd.png",
|
||||||
|
),
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -33,11 +33,7 @@ class _HomeMinePageState extends SCPageListState<FollowRoomRes, SCHomeMinePage>
|
|||||||
|
|
||||||
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
final List<Widget> _pages = [
|
final List<Widget> _pages = [SCRoomHistoryPage(), SCRoomFollowPage()];
|
||||||
SCRoomHistoryPage(),
|
|
||||||
SCRoomFollowPage(),
|
|
||||||
|
|
||||||
];
|
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
final List<Widget> _tabs = [];
|
final List<Widget> _tabs = [];
|
||||||
|
|
||||||
@ -124,7 +120,7 @@ class _HomeMinePageState extends SCPageListState<FollowRoomRes, SCHomeMinePage>
|
|||||||
physics: NeverScrollableScrollPhysics(),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
children: _pages,
|
children: _pages,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -137,7 +133,11 @@ class _HomeMinePageState extends SCPageListState<FollowRoomRes, SCHomeMinePage>
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 10.w),
|
SizedBox(width: 10.w),
|
||||||
netImage(
|
netImage(
|
||||||
url: provider.myRoom?.roomCover ?? "",
|
url: resolveRoomCoverUrl(
|
||||||
|
provider.myRoom?.id,
|
||||||
|
provider.myRoom?.roomCover,
|
||||||
|
),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
borderRadius: BorderRadius.all(Radius.circular(8.w)),
|
||||||
width: 70.w,
|
width: 70.w,
|
||||||
),
|
),
|
||||||
@ -323,7 +323,11 @@ class _HomeMinePageState extends SCPageListState<FollowRoomRes, SCHomeMinePage>
|
|||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
netImage(
|
netImage(
|
||||||
url: roomRes.roomProfile?.roomCover ?? "",
|
url: resolveRoomCoverUrl(
|
||||||
|
roomRes.roomProfile?.id,
|
||||||
|
roomRes.roomProfile?.roomCover,
|
||||||
|
),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
width: 70.w,
|
width: 70.w,
|
||||||
height: 70.w,
|
height: 70.w,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
@ -407,7 +411,11 @@ class _HomeMinePageState extends SCPageListState<FollowRoomRes, SCHomeMinePage>
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(width: 20.w),
|
SizedBox(width: 20.w),
|
||||||
netImage(
|
netImage(
|
||||||
url: res.roomProfile?.roomCover ?? "",
|
url: resolveRoomCoverUrl(
|
||||||
|
res.roomProfile?.id,
|
||||||
|
res.roomProfile?.roomCover,
|
||||||
|
),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12.w)),
|
borderRadius: BorderRadius.all(Radius.circular(12.w)),
|
||||||
width: 85.w,
|
width: 85.w,
|
||||||
|
|||||||
@ -427,7 +427,8 @@ class _HomePartyPageState extends State<SCHomePartyPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: netImage(
|
child: netImage(
|
||||||
url: res.roomCover ?? "",
|
url: resolveRoomCoverUrl(res.id, res.roomCover),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
width: 200.w,
|
width: 200.w,
|
||||||
height: 200.w,
|
height: 200.w,
|
||||||
@ -451,7 +452,7 @@ class _HomePartyPageState extends State<SCHomePartyPage>
|
|||||||
builder: (_, provider, __) {
|
builder: (_, provider, __) {
|
||||||
return netImage(
|
return netImage(
|
||||||
url:
|
url:
|
||||||
"${provider.findCountryByName(res.countryName ?? "")?.nationalFlag}",
|
"${provider.findCountryByName(res.countryName ?? "")?.nationalFlag}",
|
||||||
width: 20.w,
|
width: 20.w,
|
||||||
height: 13.w,
|
height: 13.w,
|
||||||
borderRadius: BorderRadius.circular(2.w),
|
borderRadius: BorderRadius.circular(2.w),
|
||||||
@ -503,37 +504,25 @@ class _HomePartyPageState extends State<SCHomePartyPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 5.w),
|
SizedBox(width: 5.w),
|
||||||
(res.extValues
|
(res.extValues?.roomSetting?.password?.isEmpty ?? false)
|
||||||
?.roomSetting
|
|
||||||
?.password
|
|
||||||
?.isEmpty ??
|
|
||||||
false)
|
|
||||||
? Image.asset(
|
? Image.asset(
|
||||||
"sc_images/general/sc_icon_online_user.png",
|
"sc_images/general/sc_icon_online_user.png",
|
||||||
width: 14.w,
|
width: 14.w,
|
||||||
height: 14.w,
|
height: 14.w,
|
||||||
)
|
)
|
||||||
: Image.asset(
|
: Image.asset(
|
||||||
"sc_images/index/sc_icon_room_suo.png",
|
"sc_images/index/sc_icon_room_suo.png",
|
||||||
width: 20.w,
|
width: 20.w,
|
||||||
height: 20.w,
|
height: 20.w,
|
||||||
),
|
),
|
||||||
(res.extValues
|
(res.extValues?.roomSetting?.password?.isEmpty ?? false)
|
||||||
?.roomSetting
|
|
||||||
?.password
|
|
||||||
?.isEmpty ??
|
|
||||||
false)
|
|
||||||
? SizedBox(width: 3.w)
|
? SizedBox(width: 3.w)
|
||||||
: Container(height: 10.w),
|
: Container(height: 10.w),
|
||||||
(res.extValues
|
(res.extValues?.roomSetting?.password?.isEmpty ?? false)
|
||||||
?.roomSetting
|
|
||||||
?.password
|
|
||||||
?.isEmpty ??
|
|
||||||
false)
|
|
||||||
? text(
|
? text(
|
||||||
res.extValues?.memberQuantity ?? "0",
|
res.extValues?.memberQuantity ?? "0",
|
||||||
fontSize: 12.sp,
|
fontSize: 12.sp,
|
||||||
)
|
)
|
||||||
: Container(height: 10.w),
|
: Container(height: 10.w),
|
||||||
SizedBox(width: 10.w),
|
SizedBox(width: 10.w),
|
||||||
],
|
],
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -51,7 +51,9 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
_floatingGiftListener;
|
_floatingGiftListener;
|
||||||
_tabController.addListener(() {}); // 监听切换
|
_tabController.addListener(() {}); // 监听切换
|
||||||
|
|
||||||
_subscription = eventBus.on<SCGiveRoomLuckPageDisposeEvent>().listen((event) {
|
_subscription = eventBus.on<SCGiveRoomLuckPageDisposeEvent>().listen((
|
||||||
|
event,
|
||||||
|
) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
Provider.of<GiftProvider>(context, listen: false).clearAllGiftData();
|
Provider.of<GiftProvider>(context, listen: false).clearAllGiftData();
|
||||||
Provider.of<GiftProvider>(
|
Provider.of<GiftProvider>(
|
||||||
@ -64,6 +66,10 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
final rtmProvider = Provider.of<RtmProvider>(context, listen: false);
|
||||||
|
if (rtmProvider.msgFloatingGiftListener == _floatingGiftListener) {
|
||||||
|
rtmProvider.msgFloatingGiftListener = null;
|
||||||
|
}
|
||||||
_tabController.dispose(); // 释放资源
|
_tabController.dispose(); // 释放资源
|
||||||
_subscription.cancel();
|
_subscription.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
@ -132,7 +138,7 @@ class _VoiceRoomPageState extends State<VoiceRoomPage>
|
|||||||
SizedBox(height: ScreenUtil().setWidth(5)),
|
SizedBox(height: ScreenUtil().setWidth(5)),
|
||||||
RoomOnlineUserWidget(),
|
RoomOnlineUserWidget(),
|
||||||
RoomSeatWidget(),
|
RoomSeatWidget(),
|
||||||
SizedBox(height: 2.w,),
|
SizedBox(height: 2.w),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@ -452,7 +452,8 @@ class _SearchRoomListState extends State<SearchRoomList> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: netImage(
|
child: netImage(
|
||||||
url: e.roomCover ?? "",
|
url: resolveRoomCoverUrl(e.id, e.roomCover),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
borderRadius: BorderRadius.circular(12.w),
|
borderRadius: BorderRadius.circular(12.w),
|
||||||
width: 200.w,
|
width: 200.w,
|
||||||
height: 200.w,
|
height: 200.w,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,7 @@ import '../../shared/business_logic/models/res/sc_is_follow_room_res.dart';
|
|||||||
import '../../shared/business_logic/models/res/sc_room_red_packet_list_res.dart';
|
import '../../shared/business_logic/models/res/sc_room_red_packet_list_res.dart';
|
||||||
import '../../shared/business_logic/models/res/sc_room_rocket_status_res.dart';
|
import '../../shared/business_logic/models/res/sc_room_rocket_status_res.dart';
|
||||||
import '../../shared/business_logic/models/res/sc_room_theme_list_res.dart';
|
import '../../shared/business_logic/models/res/sc_room_theme_list_res.dart';
|
||||||
|
import '../../shared/tools/sc_room_profile_cache.dart';
|
||||||
import '../../ui_kit/components/sc_float_ichart.dart';
|
import '../../ui_kit/components/sc_float_ichart.dart';
|
||||||
|
|
||||||
typedef OnSoundVoiceChange = Function(num index, int volum);
|
typedef OnSoundVoiceChange = Function(num index, int volum);
|
||||||
@ -49,7 +50,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
JoinRoomRes? currenRoom;
|
JoinRoomRes? currenRoom;
|
||||||
|
|
||||||
/// 声音音量变化监听
|
/// 声音音量变化监听
|
||||||
List<OnSoundVoiceChange> _onSoundVoiceChangeList = [];
|
final List<OnSoundVoiceChange> _onSoundVoiceChangeList = [];
|
||||||
|
|
||||||
///麦位
|
///麦位
|
||||||
Map<num, MicRes> roomWheatMap = {};
|
Map<num, MicRes> roomWheatMap = {};
|
||||||
@ -240,7 +241,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
if (num.parse(v.user!.account!) == uid) {
|
if (num.parse(v.user!.account!) == uid) {
|
||||||
roomWheatMap[k]!.setVolume = voloum;
|
roomWheatMap[k]!.setVolume = voloum;
|
||||||
for (var element in _onSoundVoiceChangeList) {
|
for (var element in _onSoundVoiceChangeList) {
|
||||||
element?.call(k, voloum);
|
element(k, voloum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,6 +268,43 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
int startTime = 0;
|
int startTime = 0;
|
||||||
|
|
||||||
|
String? _preferNonEmpty(String? primary, String? fallback) {
|
||||||
|
if ((primary ?? "").trim().isNotEmpty) {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
if ((fallback ?? "").trim().isNotEmpty) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return primary ?? fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCurrentRoomBasicInfo({
|
||||||
|
String? roomCover,
|
||||||
|
String? roomName,
|
||||||
|
String? roomDesc,
|
||||||
|
}) {
|
||||||
|
final currentRoomProfile = currenRoom?.roomProfile?.roomProfile;
|
||||||
|
if (currenRoom == null || currentRoomProfile == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currenRoom = currenRoom?.copyWith(
|
||||||
|
roomProfile: currenRoom?.roomProfile?.copyWith(
|
||||||
|
roomProfile: currentRoomProfile.copyWith(
|
||||||
|
roomCover: _preferNonEmpty(roomCover, currentRoomProfile.roomCover),
|
||||||
|
roomName: _preferNonEmpty(roomName, currentRoomProfile.roomName),
|
||||||
|
roomDesc: _preferNonEmpty(roomDesc, currentRoomProfile.roomDesc),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
SCRoomProfileCache.saveRoomProfile(
|
||||||
|
roomId: currentRoomProfile.id ?? "",
|
||||||
|
roomCover: roomCover,
|
||||||
|
roomName: roomName,
|
||||||
|
roomDesc: roomDesc,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
void joinVoiceRoomSession(
|
void joinVoiceRoomSession(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
String roomId, {
|
String roomId, {
|
||||||
@ -897,14 +935,24 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
Future<bool> loadRoomInfo(String roomId) async {
|
Future<bool> loadRoomInfo(String roomId) async {
|
||||||
try {
|
try {
|
||||||
final value = await SCChatRoomRepository().specific(roomId);
|
final value = await SCChatRoomRepository().specific(roomId);
|
||||||
|
final currentRoomProfile = currenRoom?.roomProfile?.roomProfile;
|
||||||
currenRoom = currenRoom?.copyWith(
|
currenRoom = currenRoom?.copyWith(
|
||||||
roomProfile: currenRoom?.roomProfile?.copyWith(
|
roomProfile: currenRoom?.roomProfile?.copyWith(
|
||||||
roomCounter: value.counter,
|
roomCounter: value.counter ?? currenRoom?.roomProfile?.roomCounter,
|
||||||
roomSetting: value.setting,
|
roomSetting: value.setting ?? currenRoom?.roomProfile?.roomSetting,
|
||||||
roomProfile: currenRoom?.roomProfile?.roomProfile?.copyWith(
|
roomProfile: currentRoomProfile?.copyWith(
|
||||||
roomCover: value.roomCover,
|
roomCover: _preferNonEmpty(
|
||||||
roomName: value.roomName,
|
value.roomCover,
|
||||||
roomDesc: value.roomDesc,
|
currentRoomProfile.roomCover,
|
||||||
|
),
|
||||||
|
roomName: _preferNonEmpty(
|
||||||
|
value.roomName,
|
||||||
|
currentRoomProfile.roomName,
|
||||||
|
),
|
||||||
|
roomDesc: _preferNonEmpty(
|
||||||
|
value.roomDesc,
|
||||||
|
currentRoomProfile.roomDesc,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1073,6 +1121,7 @@ class RealTimeCommunicationManager extends ChangeNotifier {
|
|||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
return seatGlobalKeyMap[index];
|
return seatGlobalKeyMap[index];
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
///播放音乐
|
///播放音乐
|
||||||
|
|||||||
@ -16,12 +16,12 @@ import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.d
|
|||||||
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/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/conversation_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
|
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
|
||||||
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart';
|
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_group_manager.dart';
|
||||||
@ -50,7 +50,7 @@ import 'package:yumi/shared/business_logic/models/res/sc_broad_cast_luck_gift_pu
|
|||||||
import 'package:yumi/shared/business_logic/models/res/broad_cast_mic_change_push.dart';
|
import 'package:yumi/shared/business_logic/models/res/broad_cast_mic_change_push.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/business_logic/models/res/sc_public_message_page_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_public_message_page_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_room_theme_list_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_room_theme_list_res.dart';
|
||||||
import 'package:yumi/ui_kit/widgets/room/invite/invite_room_dialog.dart';
|
import 'package:yumi/ui_kit/widgets/room/invite/invite_room_dialog.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:yumi/shared/business_logic/models/res/login_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||||||
@ -71,6 +71,10 @@ typedef RtmProvider = RealTimeMessagingManager;
|
|||||||
class RealTimeMessagingManager extends ChangeNotifier {
|
class RealTimeMessagingManager extends ChangeNotifier {
|
||||||
BuildContext? context;
|
BuildContext? context;
|
||||||
|
|
||||||
|
void _giftFxLog(String message) {
|
||||||
|
debugPrint('[GiftFX][RTM] $message');
|
||||||
|
}
|
||||||
|
|
||||||
///消息列表
|
///消息列表
|
||||||
List<Msg> roomAllMsgList = [];
|
List<Msg> roomAllMsgList = [];
|
||||||
List<Msg> roomChatMsgList = [];
|
List<Msg> roomChatMsgList = [];
|
||||||
@ -113,48 +117,48 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
Debouncer debouncer = Debouncer();
|
Debouncer debouncer = Debouncer();
|
||||||
List<V2TimConversation> conversationList = [];
|
List<V2TimConversation> conversationList = [];
|
||||||
|
|
||||||
///客服
|
///客服
|
||||||
SocialChatUserProfile? customerInfo;
|
SocialChatUserProfile? customerInfo;
|
||||||
|
|
||||||
int _systemUnreadCount() {
|
int _systemUnreadCount() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
||||||
count += conversationMap[conversationId]?.unreadCount ?? 0;
|
count += conversationMap[conversationId]?.unreadCount ?? 0;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
V2TimConversation getPreferredSystemConversation() {
|
V2TimConversation getPreferredSystemConversation() {
|
||||||
final systemConversations =
|
final systemConversations =
|
||||||
conversationMap.values
|
conversationMap.values
|
||||||
.where(
|
.where(
|
||||||
(element) =>
|
(element) =>
|
||||||
SCGlobalConfig.isSystemConversationId(element.conversationID),
|
SCGlobalConfig.isSystemConversationId(element.conversationID),
|
||||||
)
|
)
|
||||||
.toList()
|
.toList()
|
||||||
..sort((e1, e2) {
|
..sort((e1, e2) {
|
||||||
final time1 = e1.lastMessage?.timestamp ?? 0;
|
final time1 = e1.lastMessage?.timestamp ?? 0;
|
||||||
final time2 = e2.lastMessage?.timestamp ?? 0;
|
final time2 = e2.lastMessage?.timestamp ?? 0;
|
||||||
return time2.compareTo(time1);
|
return time2.compareTo(time1);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (systemConversations.isNotEmpty) {
|
if (systemConversations.isNotEmpty) {
|
||||||
return systemConversations.first;
|
return systemConversations.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return V2TimConversation(
|
return V2TimConversation(
|
||||||
type: ConversationType.V2TIM_C2C,
|
type: ConversationType.V2TIM_C2C,
|
||||||
userID: SCGlobalConfig.primarySystemUserId,
|
userID: SCGlobalConfig.primarySystemUserId,
|
||||||
conversationID: SCGlobalConfig.primarySystemConversationId,
|
conversationID: SCGlobalConfig.primarySystemConversationId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getConversationList() {
|
void getConversationList() {
|
||||||
List<V2TimConversation> list = conversationMap.values.toList();
|
List<V2TimConversation> list = conversationMap.values.toList();
|
||||||
list.removeWhere((element) {
|
list.removeWhere((element) {
|
||||||
if (element.conversationID == "c2c_${customerInfo?.id}") {
|
if (element.conversationID == "c2c_${customerInfo?.id}") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (element.conversationID == "c2c_atyou-newsletter") {
|
if (element.conversationID == "c2c_atyou-newsletter") {
|
||||||
///删除这个会话,后台乱发的联系人。。防止无效未读数
|
///删除这个会话,后台乱发的联系人。。防止无效未读数
|
||||||
clearC2CHistoryMessage(element.conversationID, false);
|
clearC2CHistoryMessage(element.conversationID, false);
|
||||||
@ -165,15 +169,15 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
list.sort((e1, e2) {
|
list.sort((e1, e2) {
|
||||||
int time1 = e1.lastMessage?.timestamp ?? 0;
|
int time1 = e1.lastMessage?.timestamp ?? 0;
|
||||||
int time2 = e2.lastMessage?.timestamp ?? 0;
|
int time2 = e2.lastMessage?.timestamp ?? 0;
|
||||||
return time2.compareTo(time1);
|
return time2.compareTo(time1);
|
||||||
});
|
});
|
||||||
conversationList = list;
|
conversationList = list;
|
||||||
systemUnReadCount = _systemUnreadCount();
|
systemUnReadCount = _systemUnreadCount();
|
||||||
customerUnReadCount =
|
customerUnReadCount =
|
||||||
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0;
|
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0;
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -234,11 +238,11 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
// _onRefreshConversation(conversationList);
|
// _onRefreshConversation(conversationList);
|
||||||
initConversation();
|
initConversation();
|
||||||
},
|
},
|
||||||
onTotalUnreadMessageCountChanged: (int totalUnreadCount) {
|
onTotalUnreadMessageCountChanged: (int totalUnreadCount) {
|
||||||
messageUnReadCount = totalUnreadCount;
|
messageUnReadCount = totalUnreadCount;
|
||||||
systemUnReadCount = _systemUnreadCount();
|
systemUnReadCount = _systemUnreadCount();
|
||||||
customerUnReadCount =
|
customerUnReadCount =
|
||||||
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0;
|
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0;
|
||||||
allUnReadCount =
|
allUnReadCount =
|
||||||
messageUnReadCount +
|
messageUnReadCount +
|
||||||
notifcationUnReadCount +
|
notifcationUnReadCount +
|
||||||
@ -478,16 +482,16 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (message?.userID == customerInfo?.id) {
|
if (message?.userID == customerInfo?.id) {
|
||||||
if (onNewMessageCurrentConversationListener == null) {
|
if (onNewMessageCurrentConversationListener == null) {
|
||||||
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount =
|
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount =
|
||||||
(conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0) + 1;
|
(conversationMap["c2c_${customerInfo?.id}"]?.unreadCount ?? 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
systemUnReadCount = _systemUnreadCount();
|
systemUnReadCount = _systemUnreadCount();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
onNewMessageCurrentConversationListener?.call(message, msgId: msgId);
|
onNewMessageCurrentConversationListener?.call(message, msgId: msgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onRefreshConversation(List<V2TimConversation> conversations) {
|
void _onRefreshConversation(List<V2TimConversation> conversations) {
|
||||||
for (V2TimConversation conversation in conversations) {
|
for (V2TimConversation conversation in conversations) {
|
||||||
@ -714,7 +718,9 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
} else if (SCPathUtils.getFileType(entity.path) == "video_pic") {
|
} else if (SCPathUtils.getFileType(entity.path) == "video_pic") {
|
||||||
if (entity.lengthSync() > 50000000) {
|
if (entity.lengthSync() > 50000000) {
|
||||||
SCTts.show(SCAppLocalizations.of(context!)!.theVideoSizeCannotExceed);
|
SCTts.show(
|
||||||
|
SCAppLocalizations.of(context!)!.theVideoSizeCannotExceed,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 复制一份视频
|
// 复制一份视频
|
||||||
@ -788,7 +794,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
toUser?.cleanUseProps();
|
toUser?.cleanUseProps();
|
||||||
toUser?.cleanPhotos();
|
toUser?.cleanPhotos();
|
||||||
msg.needUpDataUserInfo =
|
msg.needUpDataUserInfo =
|
||||||
Provider.of<RealTimeCommunicationManager>(context!, listen: false).needUpDataUserInfo;
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).needUpDataUserInfo;
|
||||||
msg.user = user;
|
msg.user = user;
|
||||||
msg.toUser = toUser;
|
msg.toUser = toUser;
|
||||||
final textMsg = await TencentImSDKPlugin.v2TIMManager
|
final textMsg = await TencentImSDKPlugin.v2TIMManager
|
||||||
@ -948,7 +957,8 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
///邀请进入房间
|
///邀请进入房间
|
||||||
var fdata = data["data"];
|
var fdata = data["data"];
|
||||||
SCFloatingMessage msg = SCFloatingMessage.fromJson(fdata);
|
SCFloatingMessage msg = SCFloatingMessage.fromJson(fdata);
|
||||||
if (msg.toUserId == AccountStorage().getCurrentUser()?.userProfile?.id &&
|
if (msg.toUserId ==
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.id &&
|
||||||
msg.roomId !=
|
msg.roomId !=
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
context!,
|
context!,
|
||||||
@ -1076,11 +1086,17 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
} else {
|
} else {
|
||||||
res = SCRoomThemeListRes();
|
res = SCRoomThemeListRes();
|
||||||
}
|
}
|
||||||
Provider.of<RealTimeCommunicationManager>(context!, listen: false).updateRoomBG(res);
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).updateRoomBG(res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type == SCRoomMsgType.emoticons) {
|
if (msg.type == SCRoomMsgType.emoticons) {
|
||||||
Provider.of<RealTimeCommunicationManager>(context!, listen: false).starPlayEmoji(msg);
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).starPlayEmoji(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type == SCRoomMsgType.micChange) {
|
if (msg.type == SCRoomMsgType.micChange) {
|
||||||
@ -1089,7 +1105,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
listen: false,
|
listen: false,
|
||||||
).micChange(BroadCastMicChangePush.fromJson(data).data?.mics);
|
).micChange(BroadCastMicChangePush.fromJson(data).data?.mics);
|
||||||
} else if (msg.type == SCRoomMsgType.refreshOnlineUser) {
|
} else if (msg.type == SCRoomMsgType.refreshOnlineUser) {
|
||||||
Provider.of<RealTimeCommunicationManager>(context!, listen: false).fetchOnlineUsersList();
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).fetchOnlineUsersList();
|
||||||
} else if (msg.type == SCRoomMsgType.gameLuckyGift) {
|
} else if (msg.type == SCRoomMsgType.gameLuckyGift) {
|
||||||
var broadCastRes = SCBroadCastLuckGiftPush.fromJson(data);
|
var broadCastRes = SCBroadCastLuckGiftPush.fromJson(data);
|
||||||
msg.gift = SocialChatGiftRes(giftPhoto: broadCastRes.data?.giftCover);
|
msg.gift = SocialChatGiftRes(giftPhoto: broadCastRes.data?.giftCover);
|
||||||
@ -1168,13 +1187,57 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (msg.type == SCRoomMsgType.gift) {
|
} else if (msg.type == SCRoomMsgType.gift) {
|
||||||
|
final gift = msg.gift;
|
||||||
|
final special = gift?.special ?? "";
|
||||||
|
final giftSourceUrl = gift?.giftSourceUrl ?? "";
|
||||||
|
final hasSource = giftSourceUrl.isNotEmpty;
|
||||||
|
final hasAnimation = scGiftHasAnimationSpecial(special);
|
||||||
|
final hasGlobalGift = special.contains(SCGiftType.GLOBAL_GIFT.name);
|
||||||
|
final hasFullScreenEffect = scGiftHasFullScreenEffect(special);
|
||||||
|
_giftFxLog(
|
||||||
|
'recv gift msg '
|
||||||
|
'fromUserId=${msg.user?.id} '
|
||||||
|
'fromUserName=${msg.user?.userNickname} '
|
||||||
|
'toUserId=${msg.toUser?.id} '
|
||||||
|
'toUserName=${msg.toUser?.userNickname} '
|
||||||
|
'giftId=${gift?.id} '
|
||||||
|
'giftName=${gift?.giftName} '
|
||||||
|
'giftSourceUrl=$giftSourceUrl '
|
||||||
|
'special=$special '
|
||||||
|
'hasSource=$hasSource '
|
||||||
|
'hasAnimation=$hasAnimation '
|
||||||
|
'hasGlobalGift=$hasGlobalGift '
|
||||||
|
'hasFullScreenEffect=$hasFullScreenEffect '
|
||||||
|
'effectsEnabled=${SCGlobalConfig.isGiftSpecialEffects}',
|
||||||
|
);
|
||||||
if (msg.gift!.giftSourceUrl != null && msg.gift!.special != null) {
|
if (msg.gift!.giftSourceUrl != null && msg.gift!.special != null) {
|
||||||
if (msg.gift!.special!.contains(SCGiftType.ANIMSCION.name) ||
|
if (scGiftHasFullScreenEffect(msg.gift!.special)) {
|
||||||
msg.gift!.special!.contains(SCGiftType.GLOBAL_GIFT.name)) {
|
|
||||||
if (SCGlobalConfig.isGiftSpecialEffects) {
|
if (SCGlobalConfig.isGiftSpecialEffects) {
|
||||||
|
_giftFxLog(
|
||||||
|
'trigger player play path=${msg.gift!.giftSourceUrl} '
|
||||||
|
'giftId=${msg.gift?.id} giftName=${msg.gift?.giftName}',
|
||||||
|
);
|
||||||
SCGiftVapSvgaManager().play(msg.gift!.giftSourceUrl!);
|
SCGiftVapSvgaManager().play(msg.gift!.giftSourceUrl!);
|
||||||
|
} else {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip player play because isGiftSpecialEffects=false '
|
||||||
|
'giftId=${msg.gift?.id}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip player play because special does not include '
|
||||||
|
'${SCGiftType.ANIMSCION.name}/$kSCGiftAnimationSpecialAlias/${SCGiftType.GLOBAL_GIFT.name} '
|
||||||
|
'giftId=${msg.gift?.id} special=${msg.gift?.special}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_giftFxLog(
|
||||||
|
'skip player play because giftSourceUrl or special is null '
|
||||||
|
'giftId=${msg.gift?.id} '
|
||||||
|
'giftSourceUrl=${msg.gift?.giftSourceUrl} '
|
||||||
|
'special=${msg.gift?.special}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Provider.of<RealTimeCommunicationManager>(
|
if (Provider.of<RealTimeCommunicationManager>(
|
||||||
context!,
|
context!,
|
||||||
@ -1225,7 +1288,10 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
return;
|
return;
|
||||||
} else if (msg.type == SCRoomMsgType.roomRoleChange) {
|
} else if (msg.type == SCRoomMsgType.roomRoleChange) {
|
||||||
///房间身份变动
|
///房间身份变动
|
||||||
Provider.of<RealTimeCommunicationManager>(context!, listen: false).retrieveMicrophoneList();
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
|
context!,
|
||||||
|
listen: false,
|
||||||
|
).retrieveMicrophoneList();
|
||||||
if (msg.toUser?.id ==
|
if (msg.toUser?.id ==
|
||||||
AccountStorage().getCurrentUser()?.userProfile?.id) {
|
AccountStorage().getCurrentUser()?.userProfile?.id) {
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
@ -1389,13 +1455,13 @@ class RealTimeMessagingManager extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSystemCount(int count) {
|
void updateSystemCount(int count) {
|
||||||
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
for (final conversationId in SCGlobalConfig.systemConversationIds) {
|
||||||
conversationMap[conversationId]?.unreadCount = 0;
|
conversationMap[conversationId]?.unreadCount = 0;
|
||||||
}
|
}
|
||||||
systemUnReadCount = 0;
|
systemUnReadCount = 0;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateCustomerCount(int count) {
|
void updateCustomerCount(int count) {
|
||||||
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount = 0;
|
conversationMap["c2c_${customerInfo?.id}"]?.unreadCount = 0;
|
||||||
|
|||||||
@ -1,272 +1,258 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_config_repository_imp.dart';
|
||||||
import 'package:flutter_svga/flutter_svga.dart';
|
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_config_repository_imp.dart';
|
import 'package:yumi/shared/data_sources/models/country_mode.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_banner_leaderboard_res.dart';
|
||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/business_logic/models/res/country_res.dart';
|
||||||
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
||||||
import 'package:yumi/shared/data_sources/models/country_mode.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_index_banner_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_banner_leaderboard_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_room_emoji_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/country_res.dart';
|
|
||||||
import 'package:yumi/shared/business_logic/models/res/gift_res.dart';
|
import '../../shared/data_sources/models/enum/sc_banner_type.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_index_banner_res.dart';
|
import '../../shared/data_sources/models/enum/sc_gift_type.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_room_emoji_res.dart';
|
|
||||||
|
class SCAppGeneralManager extends ChangeNotifier {
|
||||||
import '../../shared/data_sources/models/enum/sc_banner_type.dart';
|
List<CountryMode> countryModeList = [];
|
||||||
|
Map<String, List<Country>> _countryMap = {};
|
||||||
class SCAppGeneralManager extends ChangeNotifier {
|
Map<String, Country> _countryByNameMap = {};
|
||||||
List<CountryMode> countryModeList = [];
|
bool _isFetchingCountryList = false;
|
||||||
Map<String, List<Country>> _countryMap = {};
|
|
||||||
Map<String, Country> _countryByNameMap = {};
|
///礼物
|
||||||
bool _isFetchingCountryList = false;
|
List<SocialChatGiftRes> giftResList = [];
|
||||||
|
Map<String, SocialChatGiftRes> _giftByIdMap = {};
|
||||||
///礼物
|
|
||||||
List<SocialChatGiftRes> giftResList = [];
|
Map<String, List<SocialChatGiftRes>> giftByTab = {};
|
||||||
Map<String, SocialChatGiftRes> _giftByIdMap = {};
|
Map<String, List<SocialChatGiftRes>> activityGiftByTab = {};
|
||||||
|
|
||||||
Map<String, List<SocialChatGiftRes>> giftByTab = {};
|
///选中的国家
|
||||||
Map<String, List<SocialChatGiftRes>> activityGiftByTab = {};
|
Country? selectCountryInfo;
|
||||||
|
|
||||||
///选中的国家
|
List<SocialChatGiftRes> activityList = [];
|
||||||
Country? selectCountryInfo;
|
|
||||||
|
SCAppGeneralManager() {
|
||||||
List<SocialChatGiftRes> activityList = [];
|
fetchCountryList();
|
||||||
|
giftList(); // 使用默认参数
|
||||||
SCAppGeneralManager() {
|
giftActivityList();
|
||||||
fetchCountryList();
|
// giftBackpack();
|
||||||
giftList(); // 使用默认参数
|
// configLevel();
|
||||||
giftActivityList();
|
}
|
||||||
// giftBackpack();
|
|
||||||
// configLevel();
|
///加载国家列表
|
||||||
}
|
Future<void> fetchCountryList() async {
|
||||||
|
if (_isFetchingCountryList) {
|
||||||
///加载国家列表
|
return;
|
||||||
Future<void> fetchCountryList() async {
|
}
|
||||||
if (_isFetchingCountryList) {
|
if (countryModeList.isNotEmpty) {
|
||||||
return;
|
clearCountrySelection();
|
||||||
}
|
notifyListeners();
|
||||||
if (countryModeList.isNotEmpty) {
|
return;
|
||||||
clearCountrySelection();
|
}
|
||||||
notifyListeners();
|
|
||||||
return;
|
_isFetchingCountryList = true;
|
||||||
}
|
try {
|
||||||
|
await Future.delayed(Duration(milliseconds: 550));
|
||||||
_isFetchingCountryList = true;
|
final result = await SCConfigRepositoryImp().loadCountry();
|
||||||
try {
|
_countryMap.clear();
|
||||||
await Future.delayed(Duration(milliseconds: 550));
|
_countryByNameMap.clear();
|
||||||
final result = await SCConfigRepositoryImp().loadCountry();
|
countryModeList.clear();
|
||||||
_countryMap.clear();
|
|
||||||
_countryByNameMap.clear();
|
for (final c in result.openCountry ?? const <Country>[]) {
|
||||||
countryModeList.clear();
|
final prefix = c.aliasName?.substring(0, 1) ?? "";
|
||||||
|
if (prefix.isNotEmpty) {
|
||||||
for (final c in result.openCountry ?? const <Country>[]) {
|
final countryList = _countryMap[prefix] ?? <Country>[];
|
||||||
final prefix = c.aliasName?.substring(0, 1) ?? "";
|
countryList.add(c.copyWith());
|
||||||
if (prefix.isNotEmpty) {
|
_countryMap[prefix] = countryList;
|
||||||
final countryList = _countryMap[prefix] ?? <Country>[];
|
}
|
||||||
countryList.add(c.copyWith());
|
if ((c.countryName ?? "").isNotEmpty) {
|
||||||
_countryMap[prefix] = countryList;
|
_countryByNameMap[c.countryName!] = c;
|
||||||
}
|
}
|
||||||
if ((c.countryName ?? "").isNotEmpty) {
|
}
|
||||||
_countryByNameMap[c.countryName!] = c;
|
|
||||||
}
|
_countryMap.forEach((k, v) {
|
||||||
}
|
countryModeList.add(CountryMode(k, v));
|
||||||
|
});
|
||||||
_countryMap.forEach((k, v) {
|
|
||||||
countryModeList.add(CountryMode(k, v));
|
///排序
|
||||||
});
|
countryModeList.sort((a, b) => a.prefix.compareTo(b.prefix));
|
||||||
|
notifyListeners();
|
||||||
///排序
|
} catch (_) {
|
||||||
countryModeList.sort((a, b) => a.prefix.compareTo(b.prefix));
|
notifyListeners();
|
||||||
notifyListeners();
|
} finally {
|
||||||
} catch (_) {
|
_isFetchingCountryList = false;
|
||||||
notifyListeners();
|
}
|
||||||
} finally {
|
}
|
||||||
_isFetchingCountryList = false;
|
|
||||||
}
|
///国家国家名称获取国家
|
||||||
}
|
Country? findCountryByName(String countryName) {
|
||||||
|
return _countryByNameMap[countryName];
|
||||||
///国家国家名称获取国家
|
}
|
||||||
Country? findCountryByName(String countryName) {
|
|
||||||
return _countryByNameMap[countryName];
|
///选择国家
|
||||||
}
|
Country? chooseCountry(int subIndex, int cIndex) {
|
||||||
|
var selectCm = countryModeList[subIndex].prefixCountrys[cIndex];
|
||||||
///选择国家
|
if (selectCountryInfo != null) {
|
||||||
Country? chooseCountry(int subIndex, int cIndex) {
|
selectCountryInfo = null;
|
||||||
var selectCm = countryModeList[subIndex].prefixCountrys[cIndex];
|
} else {
|
||||||
if (selectCountryInfo != null) {
|
selectCountryInfo = selectCm;
|
||||||
selectCountryInfo = null;
|
}
|
||||||
} else {
|
return selectCountryInfo;
|
||||||
selectCountryInfo = selectCm;
|
}
|
||||||
}
|
|
||||||
return selectCountryInfo;
|
void updateCurrentCountry(Country? countryInfo) {
|
||||||
}
|
selectCountryInfo = countryInfo;
|
||||||
|
notifyListeners();
|
||||||
void updateCurrentCountry(Country? countryInfo) {
|
}
|
||||||
selectCountryInfo = countryInfo;
|
|
||||||
notifyListeners();
|
void clearCountrySelection() {
|
||||||
}
|
selectCountryInfo = null;
|
||||||
|
}
|
||||||
void clearCountrySelection() {
|
|
||||||
selectCountryInfo = null;
|
void openDrawer(BuildContext context) {
|
||||||
}
|
Scaffold.of(context).openDrawer();
|
||||||
|
}
|
||||||
void openDrawer(BuildContext context) {
|
|
||||||
Scaffold.of(context).openDrawer();
|
void closeDrawer(BuildContext context) {
|
||||||
}
|
Scaffold.of(context).closeDrawer();
|
||||||
|
}
|
||||||
void closeDrawer(BuildContext context) {
|
|
||||||
Scaffold.of(context).closeDrawer();
|
void giftList({bool includeCustomized = true}) async {
|
||||||
}
|
try {
|
||||||
|
giftResList = await SCChatRoomRepository().giftList();
|
||||||
void giftList({bool includeCustomized = true}) async {
|
giftByTab.clear();
|
||||||
try {
|
for (var gift in giftResList) {
|
||||||
giftResList = await SCChatRoomRepository().giftList();
|
giftByTab[gift.giftTab];
|
||||||
giftByTab.clear();
|
var gmap = giftByTab[gift.giftTab];
|
||||||
for (var gift in giftResList) {
|
gmap ??= [];
|
||||||
giftByTab[gift.giftTab];
|
gmap.add(gift);
|
||||||
var gmap = giftByTab[gift.giftTab];
|
giftByTab[gift.giftTab!] = gmap;
|
||||||
gmap ??= [];
|
var gAllMap = giftByTab["ALL"];
|
||||||
gmap.add(gift);
|
gAllMap ??= [];
|
||||||
giftByTab[gift.giftTab!] = gmap;
|
if (gift.giftTab != "NSCIONAL_FLAG" &&
|
||||||
var gAllMap = giftByTab["ALL"];
|
gift.giftTab != "ACTIVITY" &&
|
||||||
gAllMap ??= [];
|
gift.giftTab != "LUCKY_GIFT" &&
|
||||||
if (gift.giftTab != "NSCIONAL_FLAG" &&
|
gift.giftTab != "CP" &&
|
||||||
gift.giftTab != "ACTIVITY" &&
|
gift.giftTab != "CUSTOMIZED" &&
|
||||||
gift.giftTab != "LUCKY_GIFT" &&
|
gift.giftTab != "MAGIC") {
|
||||||
gift.giftTab != "CP" &&
|
gAllMap.add(gift);
|
||||||
gift.giftTab != "CUSTOMIZED" &&
|
}
|
||||||
gift.giftTab != "MAGIC") {
|
giftByTab["ALL"] = gAllMap;
|
||||||
gAllMap.add(gift);
|
_giftByIdMap[gift.id!] = gift;
|
||||||
}
|
}
|
||||||
giftByTab["ALL"] = gAllMap;
|
|
||||||
_giftByIdMap[gift.id!] = gift;
|
if (includeCustomized) {
|
||||||
}
|
giftByTab["CUSTOMIZED"] ??= [];
|
||||||
|
|
||||||
if (includeCustomized) {
|
///定制礼物需要一个占位符,显示规则的
|
||||||
giftByTab["CUSTOMIZED"] ??= [];
|
giftByTab["CUSTOMIZED"]?.insert(0, SocialChatGiftRes(id: "-1000"));
|
||||||
|
} else {
|
||||||
///定制礼物需要一个占位符,显示规则的
|
giftByTab.remove("CUSTOMIZED");
|
||||||
giftByTab["CUSTOMIZED"]?.insert(0, SocialChatGiftRes(id: "-1000"));
|
}
|
||||||
} else {
|
|
||||||
giftByTab.remove("CUSTOMIZED");
|
notifyListeners();
|
||||||
}
|
|
||||||
|
///先去预下载
|
||||||
notifyListeners();
|
downLoad(giftResList);
|
||||||
|
} catch (e) {}
|
||||||
///先去预下载
|
}
|
||||||
downLoad(giftResList);
|
|
||||||
} catch (e) {}
|
void giftActivityList() async {
|
||||||
}
|
try {
|
||||||
|
activityList.clear();
|
||||||
void giftActivityList() async {
|
activityList = await SCChatRoomRepository().giftActivityList();
|
||||||
try {
|
activityGiftByTab.clear();
|
||||||
activityList.clear();
|
for (var gift in activityList) {
|
||||||
activityList = await SCChatRoomRepository().giftActivityList();
|
var gmap = activityGiftByTab["${gift.activityId}"];
|
||||||
activityGiftByTab.clear();
|
gmap ??= [];
|
||||||
for (var gift in activityList) {
|
gmap.add(gift);
|
||||||
var gmap = activityGiftByTab["${gift.activityId}"];
|
activityGiftByTab["${gift.activityId}"] = gmap;
|
||||||
gmap ??= [];
|
}
|
||||||
gmap.add(gift);
|
notifyListeners();
|
||||||
activityGiftByTab["${gift.activityId}"] = gmap;
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
notifyListeners();
|
|
||||||
} catch (e) {}
|
///礼物背包
|
||||||
}
|
void giftBackpack() async {
|
||||||
|
try {
|
||||||
///礼物背包
|
var result = await SCChatRoomRepository().giftBackpack();
|
||||||
void giftBackpack() async {
|
} catch (e) {}
|
||||||
try {
|
}
|
||||||
var result = await SCChatRoomRepository().giftBackpack();
|
|
||||||
} catch (e) {}
|
///礼物id获取礼物
|
||||||
}
|
SocialChatGiftRes? getGiftById(String id) {
|
||||||
|
return _giftByIdMap[id];
|
||||||
///礼物id获取礼物
|
}
|
||||||
SocialChatGiftRes? getGiftById(String id) {
|
|
||||||
return _giftByIdMap[id];
|
void downLoad(List<SocialChatGiftRes> giftResList) {
|
||||||
}
|
for (var gift in giftResList) {
|
||||||
|
final giftSourceUrl = gift.giftSourceUrl ?? "";
|
||||||
void downLoad(List<SocialChatGiftRes> giftResList) {
|
if (giftSourceUrl.isEmpty) {
|
||||||
for (var gift in giftResList) {
|
continue;
|
||||||
if (gift.giftSourceUrl != null) {
|
}
|
||||||
if (SCPathUtils.getFileExtension(gift.giftSourceUrl!).toLowerCase() ==
|
if (!scGiftHasFullScreenEffect(gift.special)) {
|
||||||
".svga") {
|
continue;
|
||||||
///预解析
|
}
|
||||||
if (!SCGiftVapSvgaManager().videoItemCache.containsKey(
|
SCGiftVapSvgaManager().preload(giftSourceUrl);
|
||||||
gift.giftSourceUrl,
|
}
|
||||||
)) {
|
}
|
||||||
SVGAParser.shared.decodeFromURL(gift.giftSourceUrl!).then((entity) {
|
|
||||||
entity.autorelease = false;
|
List<SCIndexBannerRes> homeBanners = [];
|
||||||
SCGiftVapSvgaManager().videoItemCache[gift.giftSourceUrl!] =
|
List<SCIndexBannerRes> exploreBanners = [];
|
||||||
entity;
|
List<SCIndexBannerRes> roomBanners = [];
|
||||||
});
|
List<SCIndexBannerRes> gameBanners = [];
|
||||||
}
|
|
||||||
} else {
|
///首页banner
|
||||||
if ((gift.giftSourceUrl ?? "").isNotEmpty) {
|
void loadMainBanner() async {
|
||||||
FileCacheManager.getInstance().getFile(url: gift.giftSourceUrl!);
|
try {
|
||||||
}
|
var banners = await SCConfigRepositoryImp().getBanner();
|
||||||
}
|
homeBanners.clear();
|
||||||
}
|
roomBanners.clear();
|
||||||
}
|
gameBanners.clear();
|
||||||
}
|
exploreBanners.clear();
|
||||||
|
for (var v in banners) {
|
||||||
List<SCIndexBannerRes> homeBanners = [];
|
if ((v.displayPosition ?? "").contains(
|
||||||
List<SCIndexBannerRes> exploreBanners = [];
|
SCBannerType.EXPLORE_PAGE.name,
|
||||||
List<SCIndexBannerRes> roomBanners = [];
|
)) {
|
||||||
List<SCIndexBannerRes> gameBanners = [];
|
exploreBanners.add(v);
|
||||||
|
}
|
||||||
///首页banner
|
if ((v.displayPosition ?? "").contains(SCBannerType.ROOM.name)) {
|
||||||
void loadMainBanner() async {
|
roomBanners.add(v);
|
||||||
try {
|
}
|
||||||
var banners = await SCConfigRepositoryImp().getBanner();
|
|
||||||
homeBanners.clear();
|
if ((v.displayPosition ?? "").contains(SCBannerType.HOME_ALERT.name)) {
|
||||||
roomBanners.clear();
|
homeBanners.add(v);
|
||||||
gameBanners.clear();
|
}
|
||||||
exploreBanners.clear();
|
if ((v.displayPosition ?? "").contains(SCBannerType.GAME.name)) {
|
||||||
for (var v in banners) {
|
gameBanners.add(v);
|
||||||
if ((v.displayPosition ?? "").contains(
|
}
|
||||||
SCBannerType.EXPLORE_PAGE.name,
|
}
|
||||||
)) {
|
notifyListeners();
|
||||||
exploreBanners.add(v);
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
if ((v.displayPosition ?? "").contains(SCBannerType.ROOM.name)) {
|
|
||||||
roomBanners.add(v);
|
Map<String, List<Emojis>> emojiByTab = {};
|
||||||
}
|
|
||||||
|
///emoji表情
|
||||||
if ((v.displayPosition ?? "").contains(SCBannerType.HOME_ALERT.name)) {
|
void emojiAll() async {
|
||||||
homeBanners.add(v);
|
var roomEmojis = await SCChatRoomRepository().emojiAll();
|
||||||
}
|
for (var value in roomEmojis) {
|
||||||
if ((v.displayPosition ?? "").contains(SCBannerType.GAME.name)) {
|
emojiByTab[value.groupName!] = value.emojis ?? [];
|
||||||
gameBanners.add(v);
|
}
|
||||||
}
|
notifyListeners();
|
||||||
}
|
}
|
||||||
notifyListeners();
|
|
||||||
} catch (e) {}
|
///加载等级资源
|
||||||
}
|
void configLevel() {
|
||||||
|
SCConfigRepositoryImp().configLevel();
|
||||||
Map<String, List<Emojis>> emojiByTab = {};
|
}
|
||||||
|
|
||||||
///emoji表情
|
SCBannerLeaderboardRes? appLeaderResult;
|
||||||
void emojiAll() async {
|
|
||||||
var roomEmojis = await SCChatRoomRepository().emojiAll();
|
///查询榜单前三名
|
||||||
for (var value in roomEmojis) {
|
void appLeaderboard() async {
|
||||||
emojiByTab[value.groupName!] = value.emojis ?? [];
|
try {
|
||||||
}
|
appLeaderResult = await SCChatRoomRepository().appLeaderboard();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
} catch (e) {}
|
||||||
|
}
|
||||||
///加载等级资源
|
}
|
||||||
void configLevel() {
|
|
||||||
SCConfigRepositoryImp().configLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
SCBannerLeaderboardRes? appLeaderResult;
|
|
||||||
|
|
||||||
///查询榜单前三名
|
|
||||||
void appLeaderboard() async {
|
|
||||||
try {
|
|
||||||
appLeaderResult = await SCChatRoomRepository().appLeaderboard();
|
|
||||||
notifyListeners();
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -11,6 +11,9 @@ class GiftAnimationManager extends ChangeNotifier {
|
|||||||
//每个控件正在播放的动画
|
//每个控件正在播放的动画
|
||||||
Map<int, LGiftModel?> giftMap = {0: null, 1: null, 2: null, 3: null};
|
Map<int, LGiftModel?> giftMap = {0: null, 1: null, 2: null, 3: null};
|
||||||
|
|
||||||
|
bool get _controllersReady =>
|
||||||
|
animationControllerList.length >= giftMap.length;
|
||||||
|
|
||||||
void enqueueGiftAnimation(LGiftModel giftModel) {
|
void enqueueGiftAnimation(LGiftModel giftModel) {
|
||||||
pendingAnimationsQueue.add(giftModel);
|
pendingAnimationsQueue.add(giftModel);
|
||||||
proceedToNextAnimation();
|
proceedToNextAnimation();
|
||||||
@ -18,7 +21,7 @@ class GiftAnimationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
///开始播放
|
///开始播放
|
||||||
proceedToNextAnimation() {
|
proceedToNextAnimation() {
|
||||||
if (pendingAnimationsQueue.isEmpty) {
|
if (pendingAnimationsQueue.isEmpty || !_controllersReady) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var playGift = pendingAnimationsQueue.first;
|
var playGift = pendingAnimationsQueue.first;
|
||||||
@ -45,6 +48,7 @@ class GiftAnimationManager extends ChangeNotifier {
|
|||||||
|
|
||||||
void attachAnimationControllers(List<LGiftScrollingScreenAnimsBean> anins) {
|
void attachAnimationControllers(List<LGiftScrollingScreenAnimsBean> anins) {
|
||||||
animationControllerList = anins;
|
animationControllerList = anins;
|
||||||
|
proceedToNextAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupAnimationResources() {
|
void cleanupAnimationResources() {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class SocialChatRoomManager extends ChangeNotifier {
|
|||||||
|
|
||||||
void updateMyRoomInfo(SCEditRoomInfoRes roomInfo) {
|
void updateMyRoomInfo(SCEditRoomInfoRes roomInfo) {
|
||||||
if (myRoom != null && myRoom!.id == roomInfo.id) {
|
if (myRoom != null && myRoom!.id == roomInfo.id) {
|
||||||
if (roomInfo.roomCover != null) {
|
if ((roomInfo.roomCover ?? "").trim().isNotEmpty) {
|
||||||
myRoom?.setRoomCover = roomInfo.roomCover!;
|
myRoom?.setRoomCover = roomInfo.roomCover!;
|
||||||
}
|
}
|
||||||
if (roomInfo.roomName != null) {
|
if (roomInfo.roomName != null) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
|
|
||||||
import '../../../data_sources/models/enum/sc_props_type.dart';
|
import '../../../data_sources/models/enum/sc_props_type.dart';
|
||||||
|
|
||||||
/// token : ""
|
/// token : ""
|
||||||
/// userCredential : {"expireTime":0,"releaseTime":0,"sign":"","sysOrigin":"","userId":0,"version":""}
|
/// userCredential : {"expireTime":0,"releaseTime":0,"sign":"","sysOrigin":"","userId":0,"version":""}
|
||||||
/// userProfile : {"account":"","accountStatus":"","age":0,"bornDay":0,"bornMonth":0,"bornYear":0,"countryCode":"","countryId":0,"countryName":"","createTime":0,"del":false,"freezingTime":0,"id":0,"originSys":"","ownSpecialId":{"account":"","expiredTime":0},"sameRegion":false,"sysOriginChild":"","useProps":[{"expireTime":0,"propsResources":{"amount":0.0,"code":"","cover":"","expand":"","id":0,"name":"","sourceUrl":"","type":""},"userId":0}],"userAvatar":"","userNickname":"","userSex":0,"wearBadge":[{"animationUrl":"","badgeKey":"","badgeLevel":0,"badgeName":"","expireTime":0,"id":0,"milestone":0,"notSelectUrl":"","selectUrl":"","type":"","userId":0}]}
|
/// userProfile : {"account":"","accountStatus":"","age":0,"bornDay":0,"bornMonth":0,"bornYear":0,"countryCode":"","countryId":0,"countryName":"","createTime":0,"del":false,"freezingTime":0,"id":0,"originSys":"","ownSpecialId":{"account":"","expiredTime":0},"sameRegion":false,"sysOriginChild":"","useProps":[{"expireTime":0,"propsResources":{"amount":0.0,"code":"","cover":"","expand":"","id":0,"name":"","sourceUrl":"","type":""},"userId":0}],"userAvatar":"","userNickname":"","userSex":0,"wearBadge":[{"animationUrl":"","badgeKey":"","badgeLevel":0,"badgeName":"","expireTime":0,"id":0,"milestone":0,"notSelectUrl":"","selectUrl":"","type":"","userId":0}]}
|
||||||
@ -214,7 +215,7 @@ class SocialChatUserProfile {
|
|||||||
_useProps?.add(UseProps.fromJson(v));
|
_useProps?.add(UseProps.fromJson(v));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_userAvatar = json['userAvatar'];
|
_userAvatar = json['userAvatar'] ?? json['avatar'];
|
||||||
_roles = json['roles'];
|
_roles = json['roles'];
|
||||||
_userNickname = json['userNickname'];
|
_userNickname = json['userNickname'];
|
||||||
_hobby = json['hobby'];
|
_hobby = json['hobby'];
|
||||||
@ -462,6 +463,7 @@ class SocialChatUserProfile {
|
|||||||
void cleanWearBadge() {
|
void cleanWearBadge() {
|
||||||
_wearBadge?.clear();
|
_wearBadge?.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanUseProps() {
|
void cleanUseProps() {
|
||||||
_useProps?.clear();
|
_useProps?.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ class SCEditRoomInfoRes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SCEditRoomInfoRes.fromJson(dynamic json) {
|
SCEditRoomInfoRes.fromJson(dynamic json) {
|
||||||
_id = json['id'];
|
_id = json['id'] ?? json['roomId'];
|
||||||
_roomAccount = json['roomAccount'];
|
_roomAccount = json['roomAccount'];
|
||||||
_userId = json['userId'];
|
_userId = json['userId'];
|
||||||
_roomCover = json['roomCover'] ?? json['cover'];
|
_roomCover = json['roomCover'] ?? json['cover'];
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
enum SCGiftType{
|
enum SCGiftType { ANIMSCION, MUSIC, GLOBAL_GIFT, LUCKY_GIFT, MAGIC, CP }
|
||||||
ANIMSCION,
|
|
||||||
MUSIC,
|
const String kSCGiftAnimationSpecialAlias = 'ANIMATION';
|
||||||
GLOBAL_GIFT,
|
|
||||||
LUCKY_GIFT,
|
bool scGiftHasAnimationSpecial(String? special) {
|
||||||
MAGIC,
|
final value = special ?? '';
|
||||||
CP,
|
return value.contains(SCGiftType.ANIMSCION.name) ||
|
||||||
}
|
value.contains(kSCGiftAnimationSpecialAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scGiftHasFullScreenEffect(String? special) {
|
||||||
|
final value = special ?? '';
|
||||||
|
return scGiftHasAnimationSpecial(value) ||
|
||||||
|
value.contains(SCGiftType.GLOBAL_GIFT.name);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,26 +1,29 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:yumi/shared/business_logic/repositories/general_repository.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/remote/net/api.dart';
|
import 'package:yumi/shared/business_logic/repositories/general_repository.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
import 'package:yumi/shared/data_sources/sources/remote/net/api.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
||||||
class SCGeneralRepositoryImp implements SocialChatGeneralRepository {
|
|
||||||
static SCGeneralRepositoryImp? _instance;
|
class SCGeneralRepositoryImp implements SocialChatGeneralRepository {
|
||||||
|
static SCGeneralRepositoryImp? _instance;
|
||||||
SCGeneralRepositoryImp._internal();
|
|
||||||
|
SCGeneralRepositoryImp._internal();
|
||||||
factory SCGeneralRepositoryImp() {
|
|
||||||
return _instance ??= SCGeneralRepositoryImp._internal();
|
factory SCGeneralRepositoryImp() {
|
||||||
}
|
return _instance ??= SCGeneralRepositoryImp._internal();
|
||||||
|
}
|
||||||
|
|
||||||
///external/oss/upload
|
///external/oss/upload
|
||||||
@override
|
@override
|
||||||
Future<String> upload(File image) async {
|
Future<String> upload(File image) async {
|
||||||
String filePath = image.path;
|
String filePath = image.path;
|
||||||
var name =
|
var name = filePath.substring(
|
||||||
filePath.substring(filePath.lastIndexOf("/") + 1, filePath.length);
|
filePath.lastIndexOf("/") + 1,
|
||||||
FormData formData = FormData.fromMap(
|
filePath.length,
|
||||||
{"file": await MultipartFile.fromFile(filePath, filename: name)},
|
|
||||||
);
|
);
|
||||||
|
FormData formData = FormData.fromMap({
|
||||||
|
"file": await MultipartFile.fromFile(filePath, filename: name),
|
||||||
|
});
|
||||||
final response = await http.dio.post(
|
final response = await http.dio.post(
|
||||||
"91458261d1ab337ad7b1da3610dd496cf7f58dbb0fecacfbdfe516c4892b1694",
|
"91458261d1ab337ad7b1da3610dd496cf7f58dbb0fecacfbdfe516c4892b1694",
|
||||||
data: formData,
|
data: formData,
|
||||||
@ -28,15 +31,15 @@ class SCGeneralRepositoryImp implements SocialChatGeneralRepository {
|
|||||||
contentType: 'multipart/form-data',
|
contentType: 'multipart/form-data',
|
||||||
sendTimeout: const Duration(seconds: 60),
|
sendTimeout: const Duration(seconds: 60),
|
||||||
receiveTimeout: const Duration(seconds: 60),
|
receiveTimeout: const Duration(seconds: 60),
|
||||||
extra: {
|
extra: {TimeOutInterceptor.timeoutSecondsKey: 60},
|
||||||
TimeOutInterceptor.timeoutSecondsKey: 60,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final baseResponse = ResponseData<String>.fromJson(
|
final baseResponse = ResponseData<String>.fromJson(
|
||||||
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("[OSS Upload] parsed file url: ${baseResponse.body ?? ""}");
|
||||||
return baseResponse.body ?? "";
|
return baseResponse.body ?? "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import 'package:yumi/shared/business_logic/models/res/sc_violation_handle_res.da
|
|||||||
import 'package:yumi/shared/business_logic/repositories/room_repository.dart';
|
import 'package:yumi/shared/business_logic/repositories/room_repository.dart';
|
||||||
import 'package:yumi/services/audio/rtm_manager.dart';
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_room_profile_cache.dart';
|
||||||
|
|
||||||
class SCChatRoomRepository implements SocialChatRoomRepository {
|
class SCChatRoomRepository implements SocialChatRoomRepository {
|
||||||
static SCChatRoomRepository? _instance;
|
static SCChatRoomRepository? _instance;
|
||||||
@ -55,9 +56,10 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
"363603111e51beac2d183014dd29b81ced652239ec13178acd7001096cab7429",
|
"363603111e51beac2d183014dd29b81ced652239ec13178acd7001096cab7429",
|
||||||
queryParams: queryParams,
|
queryParams: queryParams,
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) => (json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
(json) =>
|
||||||
|
(json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result.map(SCRoomProfileCache.applyToSocialChatRoomRes).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
///live/mic/list
|
///live/mic/list
|
||||||
@ -84,7 +86,7 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
queryParams: queryParams,
|
queryParams: queryParams,
|
||||||
fromJson: (json) => MyRoomRes.fromJson(json),
|
fromJson: (json) => MyRoomRes.fromJson(json),
|
||||||
);
|
);
|
||||||
return result;
|
return SCRoomProfileCache.applyToMyRoomRes(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/relation/status
|
///room/relation/status
|
||||||
@ -115,7 +117,9 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
|
|
||||||
///activity/room-contribution-activity
|
///activity/room-contribution-activity
|
||||||
@override
|
@override
|
||||||
Future<SCRoomContributeLevelRes> roomContributionActivity(String roomId) async {
|
Future<SCRoomContributeLevelRes> roomContributionActivity(
|
||||||
|
String roomId,
|
||||||
|
) async {
|
||||||
Map<String, dynamic> queryParams = {};
|
Map<String, dynamic> queryParams = {};
|
||||||
queryParams["roomId"] = roomId;
|
queryParams["roomId"] = roomId;
|
||||||
final result = await http.get(
|
final result = await http.get(
|
||||||
@ -191,7 +195,10 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
"a01ade8fbb604c0904557a9483fcc4d9",
|
"a01ade8fbb604c0904557a9483fcc4d9",
|
||||||
queryParams: {"roomId": roomId},
|
queryParams: {"roomId": roomId},
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) => (json as List).map((e) => SocialChatUserProfile.fromJson(e)).toList(),
|
(json) =>
|
||||||
|
(json as List)
|
||||||
|
.map((e) => SocialChatUserProfile.fromJson(e))
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -213,7 +220,8 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
final result = await http.get<List<SocialChatGiftRes>>(
|
final result = await http.get<List<SocialChatGiftRes>>(
|
||||||
"3a613b7450f8fd9082b988bd21df454d",
|
"3a613b7450f8fd9082b988bd21df454d",
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) => (json as List).map((e) => SocialChatGiftRes.fromJson(e)).toList(),
|
(json) =>
|
||||||
|
(json as List).map((e) => SocialChatGiftRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -317,9 +325,10 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
"363603111e51beac2d183014dd29b81cce9dda0f9cd8c267f6e4fd3f05bda090",
|
"363603111e51beac2d183014dd29b81cce9dda0f9cd8c267f6e4fd3f05bda090",
|
||||||
queryParams: {"roomAccount": roomAccount},
|
queryParams: {"roomAccount": roomAccount},
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) => (json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
(json) =>
|
||||||
|
(json as List).map((e) => SocialChatRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result.map(SCRoomProfileCache.applyToSocialChatRoomRes).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/blacklist/join
|
///room/blacklist/join
|
||||||
@ -500,7 +509,9 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
queryParams: parm,
|
queryParams: parm,
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => SocialChatRoomMemberRes.fromJson(e)).toList(),
|
(json as List)
|
||||||
|
.map((e) => SocialChatRoomMemberRes.fromJson(e))
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -584,7 +595,9 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
"48ab0afa0e4305070eaa82edc3fc3996",
|
"48ab0afa0e4305070eaa82edc3fc3996",
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => SocialChatGiftBackpackRes.fromJson(e)).toList(),
|
(json as List)
|
||||||
|
.map((e) => SocialChatGiftBackpackRes.fromJson(e))
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -775,7 +788,8 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
final result = await http.get<List<SocialChatGiftRes>>(
|
final result = await http.get<List<SocialChatGiftRes>>(
|
||||||
"60296415428765e04263c733bfc8428c397bb180e31856d94bf25d85eb342787",
|
"60296415428765e04263c733bfc8428c397bb180e31856d94bf25d85eb342787",
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) => (json as List).map((e) => SocialChatGiftRes.fromJson(e)).toList(),
|
(json) =>
|
||||||
|
(json as List).map((e) => SocialChatGiftRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -815,11 +829,4 @@ class SCChatRoomRepository implements SocialChatRoomRepository {
|
|||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import 'package:yumi/shared/business_logic/models/res/sc_gold_record_res.dart';
|
|||||||
import 'package:yumi/shared/business_logic/models/res/sc_prop_coupon_list_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_prop_coupon_list_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_prop_coupon_record_list_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_prop_coupon_record_list_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_public_message_page_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_public_message_page_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/room_res.dart' hide WearBadge;
|
import 'package:yumi/shared/business_logic/models/res/room_res.dart'
|
||||||
|
hide WearBadge;
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_rtc_token_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_rtc_token_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_task_list_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_task_list_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_user_counter_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_user_counter_res.dart';
|
||||||
@ -13,9 +14,12 @@ import 'package:yumi/shared/business_logic/models/res/sc_user_red_packet_grab_re
|
|||||||
import 'package:yumi/shared/business_logic/models/res/sc_user_red_packet_send_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_user_red_packet_send_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/req/sc_mobile_auth_cmd.dart';
|
import 'package:yumi/shared/business_logic/models/req/sc_mobile_auth_cmd.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/sc_edit_room_info_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_edit_room_info_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/follow_room_res.dart' hide WearBadge;
|
import 'package:yumi/shared/business_logic/models/res/follow_room_res.dart'
|
||||||
import 'package:yumi/shared/business_logic/models/res/follow_user_res.dart' hide WearBadge;
|
hide WearBadge;
|
||||||
import 'package:yumi/shared/business_logic/models/res/join_room_res.dart' hide WearBadge;
|
import 'package:yumi/shared/business_logic/models/res/follow_user_res.dart'
|
||||||
|
hide WearBadge;
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/join_room_res.dart'
|
||||||
|
hide WearBadge;
|
||||||
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/message_friend_user_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/message_friend_user_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/models/res/my_room_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/my_room_res.dart';
|
||||||
@ -24,6 +28,7 @@ import 'package:yumi/shared/business_logic/models/res/sc_sign_in_res.dart';
|
|||||||
import 'package:yumi/shared/business_logic/models/res/sc_violation_handle_res.dart';
|
import 'package:yumi/shared/business_logic/models/res/sc_violation_handle_res.dart';
|
||||||
import 'package:yumi/shared/business_logic/repositories/user_repository.dart';
|
import 'package:yumi/shared/business_logic/repositories/user_repository.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
import 'package:yumi/shared/data_sources/sources/remote/net/network_client.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_room_profile_cache.dart';
|
||||||
|
|
||||||
class SCAccountRepository implements SocialChatUserRepository {
|
class SCAccountRepository implements SocialChatUserRepository {
|
||||||
static SCAccountRepository? _instance;
|
static SCAccountRepository? _instance;
|
||||||
@ -83,7 +88,10 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
|
|
||||||
///auth/account/login/channel
|
///auth/account/login/channel
|
||||||
@override
|
@override
|
||||||
Future<SocialChatLoginRes> loginForChannel(String authType, String openId) async {
|
Future<SocialChatLoginRes> loginForChannel(
|
||||||
|
String authType,
|
||||||
|
String openId,
|
||||||
|
) async {
|
||||||
final result = await http.post<SocialChatLoginRes>(
|
final result = await http.post<SocialChatLoginRes>(
|
||||||
"e64f27b9ba6b37881120f4584a5444a5531a33eb137749a5decad584f58aaa44",
|
"e64f27b9ba6b37881120f4584a5444a5531a33eb137749a5decad584f58aaa44",
|
||||||
data: {"authType": authType, "openId": openId},
|
data: {"authType": authType, "openId": openId},
|
||||||
@ -106,7 +114,7 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => FollowRoomRes.fromJson(e)).toList(),
|
(json as List).map((e) => FollowRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result.map(SCRoomProfileCache.applyToFollowRoomRes).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/relation/joined
|
///room/relation/joined
|
||||||
@ -118,7 +126,7 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => FollowRoomRes.fromJson(e)).toList(),
|
(json as List).map((e) => FollowRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result.map(SCRoomProfileCache.applyToFollowRoomRes).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/relation/trace
|
///room/relation/trace
|
||||||
@ -135,7 +143,7 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
(json) =>
|
(json) =>
|
||||||
(json as List).map((e) => FollowRoomRes.fromJson(e)).toList(),
|
(json as List).map((e) => FollowRoomRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result.map(SCRoomProfileCache.applyToFollowRoomRes).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/profile
|
///room/profile
|
||||||
@ -145,7 +153,7 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
"1e41384e55cbd6c2374608b129e2ed27",
|
"1e41384e55cbd6c2374608b129e2ed27",
|
||||||
fromJson: (json) => MyRoomRes.fromJson(json),
|
fromJson: (json) => MyRoomRes.fromJson(json),
|
||||||
);
|
);
|
||||||
return result;
|
return SCRoomProfileCache.applyToMyRoomRes(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
///room/live-voice/create
|
///room/live-voice/create
|
||||||
@ -179,7 +187,7 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
data: param,
|
data: param,
|
||||||
fromJson: (json) => JoinRoomRes.fromJson(json),
|
fromJson: (json) => JoinRoomRes.fromJson(json),
|
||||||
);
|
);
|
||||||
return result;
|
return SCRoomProfileCache.applyToJoinRoomRes(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
///live/user/heartbeat
|
///live/user/heartbeat
|
||||||
@ -235,13 +243,21 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
"1e41384e55cbd6c2374608b129e2ed27",
|
"1e41384e55cbd6c2374608b129e2ed27",
|
||||||
data: {
|
data: {
|
||||||
"id": roomId,
|
"id": roomId,
|
||||||
|
"roomId": roomId,
|
||||||
"roomCover": roomCover,
|
"roomCover": roomCover,
|
||||||
|
"cover": roomCover,
|
||||||
"roomName": roomName,
|
"roomName": roomName,
|
||||||
"roomDesc": roomDesc,
|
"roomDesc": roomDesc,
|
||||||
"event": event,
|
"event": event,
|
||||||
},
|
},
|
||||||
fromJson: (json) => SCEditRoomInfoRes.fromJson(json),
|
fromJson: (json) => SCEditRoomInfoRes.fromJson(json),
|
||||||
);
|
);
|
||||||
|
await SCRoomProfileCache.saveRoomProfile(
|
||||||
|
roomId: roomId,
|
||||||
|
roomCover: roomCover,
|
||||||
|
roomName: roomName,
|
||||||
|
roomDesc: roomDesc,
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,10 +447,10 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
parm["personalPhotos"] = personalPhotos;
|
parm["personalPhotos"] = personalPhotos;
|
||||||
}
|
}
|
||||||
|
|
||||||
final result = await http.put<SocialChatUserProfile>(
|
final result = await http.put<SocialChatUserProfile>(
|
||||||
"2fa1d4f56bb726558904ce2a50b83f96365e62b6aa808acde7b56d2d5945fbe4",
|
"2fa1d4f56bb726558904ce2a50b83f96365e62b6aa808acde7b56d2d5945fbe4",
|
||||||
data: parm,
|
data: parm,
|
||||||
fromJson: (json) => SocialChatUserProfile.fromJson(json),
|
fromJson: (json) => SocialChatUserProfile.fromJson(json),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -535,7 +551,6 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///user/user-level/consumption/exp
|
///user/user-level/consumption/exp
|
||||||
@override
|
@override
|
||||||
Future<SCUserLevelExpRes> userLevelConsumptionExp(
|
Future<SCUserLevelExpRes> userLevelConsumptionExp(
|
||||||
@ -757,7 +772,8 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
final result = await http.get<List<SCTaskListRes>>(
|
final result = await http.get<List<SCTaskListRes>>(
|
||||||
"6e87048da643aa0388a9e46ea391daff574aff257ce7e668d08f4caccd1c6232",
|
"6e87048da643aa0388a9e46ea391daff574aff257ce7e668d08f4caccd1c6232",
|
||||||
fromJson:
|
fromJson:
|
||||||
(json) => (json as List).map((e) => SCTaskListRes.fromJson(e)).toList(),
|
(json) =>
|
||||||
|
(json as List).map((e) => SCTaskListRes.fromJson(e)).toList(),
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -915,7 +931,6 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///user/cp-relationship/create-apply
|
///user/cp-relationship/create-apply
|
||||||
@override
|
@override
|
||||||
Future<bool> cpRelationshipSendApply(String acceptApplyUserId) async {
|
Future<bool> cpRelationshipSendApply(String acceptApplyUserId) async {
|
||||||
@ -1086,7 +1101,6 @@ class SCAccountRepository implements SocialChatUserRepository {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///user/vip/ability/update
|
///user/vip/ability/update
|
||||||
@override
|
@override
|
||||||
Future<bool> userVipAbilityUpdate({
|
Future<bool> userVipAbilityUpdate({
|
||||||
|
|||||||
@ -1,305 +1,567 @@
|
|||||||
import 'package:flutter/animation.dart';
|
import 'dart:async';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'dart:collection';
|
||||||
import 'package:flutter_svga/flutter_svga.dart';
|
import 'dart:io';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
import 'package:flutter_svga/flutter_svga.dart';
|
||||||
import 'package:tancent_vap/utils/constant.dart';
|
import 'package:yumi/app/constants/sc_global_config.dart';
|
||||||
import 'package:tancent_vap/widgets/vap_view.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/file_cache_manager.dart';
|
||||||
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
import 'package:tancent_vap/utils/constant.dart';
|
||||||
|
import 'package:tancent_vap/widgets/vap_view.dart';
|
||||||
class SCGiftVapSvgaManager {
|
|
||||||
Map<String, MovieEntity> videoItemCache = {};
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
static SCGiftVapSvgaManager? _inst;
|
|
||||||
|
class SCGiftVapSvgaManager {
|
||||||
SCGiftVapSvgaManager._internal();
|
Map<String, MovieEntity> videoItemCache = {};
|
||||||
|
static SCGiftVapSvgaManager? _inst;
|
||||||
factory SCGiftVapSvgaManager() => _inst ??= SCGiftVapSvgaManager._internal();
|
static const int _maxPreloadConcurrency = 1;
|
||||||
|
|
||||||
final SCPriorityQueue<SCVapTask> _tq = SCPriorityQueue<SCVapTask>(
|
SCGiftVapSvgaManager._internal();
|
||||||
(a, b) => a.compareTo(b), // 调用 SCVapTask 的 compareTo 方法
|
|
||||||
);
|
factory SCGiftVapSvgaManager() => _inst ??= SCGiftVapSvgaManager._internal();
|
||||||
VapController? _rgc;
|
|
||||||
SVGAAnimationController? _rsc;
|
final SCPriorityQueue<SCVapTask> _tq = SCPriorityQueue<SCVapTask>(
|
||||||
bool _play = false;
|
(a, b) => a.compareTo(b), // 调用 SCVapTask 的 compareTo 方法
|
||||||
bool _dis = false;
|
);
|
||||||
|
VapController? _rgc;
|
||||||
bool _pause = false;
|
SVGAAnimationController? _rsc;
|
||||||
|
bool _play = false;
|
||||||
//是否关闭礼物特效声音
|
bool _dis = false;
|
||||||
bool _mute = false;
|
SCVapTask? _currentTask;
|
||||||
|
final Queue<String> _preloadQueue = Queue<String>();
|
||||||
void setMute(bool muteMusic) {
|
final Set<String> _queuedPreloadPaths = <String>{};
|
||||||
_mute = muteMusic;
|
final Map<String, Future<MovieEntity>> _svgaLoadTasks = {};
|
||||||
DataPersistence.setPlayGiftMusic(_mute);
|
final Map<String, Future<String>> _playablePathTasks = {};
|
||||||
}
|
final Map<String, String> _playablePathCache = {};
|
||||||
|
int _activePreloadCount = 0;
|
||||||
bool getMute() {
|
|
||||||
return _mute;
|
bool _pause = false;
|
||||||
}
|
|
||||||
|
//是否关闭礼物特效声音
|
||||||
// 绑定控制器
|
bool _mute = false;
|
||||||
void bindVapCtrl(VapController vapController) {
|
|
||||||
_mute = DataPersistence.getPlayGiftMusic();
|
void setMute(bool muteMusic) {
|
||||||
_dis = false;
|
_mute = muteMusic;
|
||||||
_rgc = vapController;
|
DataPersistence.setPlayGiftMusic(_mute);
|
||||||
_rgc?.setAnimListener(
|
}
|
||||||
onVideoStart: () {
|
|
||||||
},
|
bool getMute() {
|
||||||
onVideoComplete: () {
|
return _mute;
|
||||||
_hcs();
|
}
|
||||||
},
|
|
||||||
onFailed: (code, type, msg) {
|
void _log(String message) {
|
||||||
_hcs();
|
debugPrint('[GiftFX][Player] $message');
|
||||||
},
|
}
|
||||||
);
|
|
||||||
}
|
bool _needsSvgaController(String path) {
|
||||||
|
return SCPathUtils.getFileExtension(path).toLowerCase() == ".svga";
|
||||||
void bindSvgaCtrl(SVGAAnimationController svgaController) {
|
}
|
||||||
_dis = false;
|
|
||||||
_rsc = svgaController;
|
bool _isControllerReady(SCVapTask task) {
|
||||||
_rsc?.addStatusListener((AnimationStatus status) {
|
if (_needsSvgaController(task.path)) {
|
||||||
if (status.isCompleted) {
|
return _rsc != null;
|
||||||
_rsc?.reset();
|
}
|
||||||
_play = false;
|
return _rgc != null;
|
||||||
_pn();
|
}
|
||||||
}
|
|
||||||
});
|
bool _isPlayableFileReady(String path) {
|
||||||
}
|
final cachedPath = _playablePathCache[path];
|
||||||
|
if (cachedPath == null || cachedPath.isEmpty) {
|
||||||
// 播放任务
|
return false;
|
||||||
void play(
|
}
|
||||||
String path, {
|
return File(cachedPath).existsSync();
|
||||||
int priority = 0,
|
}
|
||||||
Map<String, VAPContent>? customResources,
|
|
||||||
int type = 0,
|
bool _isPreloadedOrLoading(String path) {
|
||||||
}) {
|
if (_needsSvgaController(path)) {
|
||||||
if (path.isEmpty) {
|
return videoItemCache.containsKey(path) ||
|
||||||
return;
|
_svgaLoadTasks.containsKey(path);
|
||||||
}
|
}
|
||||||
if (SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim) {
|
final pathType = SCPathUtils.getPathType(path);
|
||||||
if (_dis) return;
|
if (pathType == PathType.asset || pathType == PathType.file) {
|
||||||
final task = SCVapTask(
|
return true;
|
||||||
path: path,
|
}
|
||||||
priority: priority,
|
return _isPlayableFileReady(path) || _playablePathTasks.containsKey(path);
|
||||||
customResources: customResources,
|
}
|
||||||
);
|
|
||||||
|
Future<void> preload(String path, {bool highPriority = false}) async {
|
||||||
_tq.add(task);
|
if (path.isEmpty || _dis || _isPreloadedOrLoading(path)) {
|
||||||
if (!_play) {
|
return;
|
||||||
_pn();
|
}
|
||||||
}
|
if (highPriority) {
|
||||||
}
|
_log('high priority preload path=$path');
|
||||||
}
|
await _warmupPath(path);
|
||||||
|
return;
|
||||||
// 播放下一个任务
|
}
|
||||||
Future<void> _pn() async {
|
if (_queuedPreloadPaths.contains(path)) {
|
||||||
if (_pause) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
_preloadQueue.add(path);
|
||||||
if (_dis || _tq.isEmpty || _play) return;
|
_queuedPreloadPaths.add(path);
|
||||||
|
_log('enqueue preload path=$path queue=${_preloadQueue.length}');
|
||||||
final task = _tq.removeFirst();
|
_drainPreloadQueue();
|
||||||
_play = true;
|
}
|
||||||
try {
|
|
||||||
final pathType = SCPathUtils.getPathType(task.path);
|
void _drainPreloadQueue() {
|
||||||
if (pathType == PathType.asset) {
|
if (_dis ||
|
||||||
await _pa(task);
|
_pause ||
|
||||||
} else if (pathType == PathType.file) {
|
_play ||
|
||||||
await _pf(task);
|
_activePreloadCount >= _maxPreloadConcurrency ||
|
||||||
} else if (pathType == PathType.network) {
|
_preloadQueue.isEmpty) {
|
||||||
await _pnw(task);
|
return;
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
final path = _preloadQueue.removeFirst();
|
||||||
print('VAP_SVGA播放失败: $e\n$s');
|
_queuedPreloadPaths.remove(path);
|
||||||
} finally {
|
_activePreloadCount++;
|
||||||
// 确保状态正确重置
|
_log(
|
||||||
// if (!_dis) {
|
'start preload path=$path active=$_activePreloadCount '
|
||||||
// _pn();
|
'queueRemaining=${_preloadQueue.length}',
|
||||||
// }
|
);
|
||||||
}
|
_warmupPath(path).whenComplete(() {
|
||||||
}
|
_activePreloadCount--;
|
||||||
|
_log(
|
||||||
// 播放资源文件
|
'finish preload path=$path active=$_activePreloadCount '
|
||||||
Future<void> _pa(SCVapTask task) async {
|
'queueRemaining=${_preloadQueue.length}',
|
||||||
if (_dis) return;
|
);
|
||||||
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
|
_drainPreloadQueue();
|
||||||
MovieEntity entity;
|
});
|
||||||
if (videoItemCache.containsKey(task.path)) {
|
}
|
||||||
entity = videoItemCache[task.path]!;
|
|
||||||
} else {
|
Future<void> _warmupPath(String path) async {
|
||||||
entity = await SVGAParser.shared.decodeFromAssets(task.path);
|
if (_needsSvgaController(path)) {
|
||||||
videoItemCache[task.path] = entity;
|
await _loadSvgaEntity(path);
|
||||||
}
|
return;
|
||||||
_rsc?.videoItem = entity;
|
}
|
||||||
_rsc?.reset();
|
await _ensurePlayableFilePath(path);
|
||||||
_rsc?.forward();
|
}
|
||||||
} else {
|
|
||||||
if (task.customResources != null) {
|
Future<MovieEntity> _loadSvgaEntity(String path) async {
|
||||||
task.customResources?.forEach((k, v) async {
|
final cached = videoItemCache[path];
|
||||||
await _rgc?.setVapTagContent(k, v);
|
if (cached != null) {
|
||||||
});
|
return cached;
|
||||||
}
|
}
|
||||||
await _rgc?.playAsset(task.path);
|
final loadingTask = _svgaLoadTasks[path];
|
||||||
}
|
if (loadingTask != null) {
|
||||||
}
|
return loadingTask;
|
||||||
|
}
|
||||||
// 播放本地文件
|
final future = () async {
|
||||||
Future<void> _pf(SCVapTask task) async {
|
final pathType = SCPathUtils.getPathType(path);
|
||||||
if (_dis || _rgc == null) return;
|
late final MovieEntity entity;
|
||||||
await _rgc?.setMute(_mute);
|
if (pathType == PathType.asset) {
|
||||||
if (task.customResources != null) {
|
entity = await SVGAParser.shared.decodeFromAssets(path);
|
||||||
task.customResources?.forEach((k, v) async {
|
} else if (pathType == PathType.network) {
|
||||||
await _rgc?.setVapTagContent(k, v);
|
entity = await SVGAParser.shared.decodeFromURL(path);
|
||||||
});
|
} else if (pathType == PathType.file) {
|
||||||
}
|
final bytes = await File(path).readAsBytes();
|
||||||
await _rgc!.playFile(task.path);
|
entity = await SVGAParser.shared.decodeFromBuffer(bytes);
|
||||||
}
|
} else {
|
||||||
|
throw Exception('Unsupported SVGA path: $path');
|
||||||
// 播放网络资源
|
}
|
||||||
Future<void> _pnw(SCVapTask task) async {
|
entity.autorelease = false;
|
||||||
if (_dis) return;
|
videoItemCache[path] = entity;
|
||||||
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
|
return entity;
|
||||||
MovieEntity? entity;
|
}();
|
||||||
if (videoItemCache.containsKey(task.path)) {
|
_svgaLoadTasks[path] = future;
|
||||||
entity = videoItemCache[task.path]!;
|
try {
|
||||||
entity.autorelease = false;
|
return await future;
|
||||||
} else {
|
} finally {
|
||||||
try {
|
_svgaLoadTasks.remove(path);
|
||||||
entity = await SVGAParser.shared.decodeFromURL(task.path);
|
}
|
||||||
entity.autorelease = false;
|
}
|
||||||
videoItemCache[task.path] = entity;
|
|
||||||
} catch (e) {
|
Future<String> _ensurePlayableFilePath(String path) async {
|
||||||
_play = false;
|
final pathType = SCPathUtils.getPathType(path);
|
||||||
print('svga解析出错:$e');
|
if (pathType == PathType.asset || pathType == PathType.file) {
|
||||||
}
|
return path;
|
||||||
}
|
}
|
||||||
if (entity != null) {
|
final cachedPath = _playablePathCache[path];
|
||||||
_rsc?.videoItem = entity;
|
if (cachedPath != null &&
|
||||||
_rsc?.reset();
|
cachedPath.isNotEmpty &&
|
||||||
_rsc?.forward();
|
File(cachedPath).existsSync()) {
|
||||||
}
|
return cachedPath;
|
||||||
} else {
|
}
|
||||||
final file = await FileCacheManager.getInstance().getFile(url: task.path);
|
final loadingTask = _playablePathTasks[path];
|
||||||
if (file != null && !_dis) {
|
if (loadingTask != null) {
|
||||||
await _pf(
|
return loadingTask;
|
||||||
SCVapTask(
|
}
|
||||||
path: file.path,
|
final future = () async {
|
||||||
priority: task.priority,
|
final file = await FileCacheManager.getInstance().getFile(url: path);
|
||||||
customResources: task.customResources,
|
_playablePathCache[path] = file.path;
|
||||||
),
|
return file.path;
|
||||||
);
|
}();
|
||||||
}
|
_playablePathTasks[path] = future;
|
||||||
}
|
try {
|
||||||
}
|
return await future;
|
||||||
|
} finally {
|
||||||
// 处理控制器状态变化
|
_playablePathTasks.remove(path);
|
||||||
void _hcs() {
|
}
|
||||||
if (_rgc != null && !_dis) {
|
}
|
||||||
_play = false;
|
|
||||||
// 延迟一小段时间后播放下一个,避免状态冲突
|
void _scheduleNextTask({Duration delay = Duration.zero}) {
|
||||||
Future.delayed(const Duration(milliseconds: 50), _pn);
|
if (_dis) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
if (delay == Duration.zero) {
|
||||||
//暂停动画播放
|
Future.microtask(_pn);
|
||||||
void pauseAnim() {
|
return;
|
||||||
_pause = true;
|
}
|
||||||
}
|
Future.delayed(delay, _pn);
|
||||||
|
}
|
||||||
//恢复动画播放
|
|
||||||
void resumeAnim() {
|
void _finishCurrentTask({Duration delay = Duration.zero}) {
|
||||||
_pause = false;
|
if (_dis) {
|
||||||
_pn();
|
return;
|
||||||
}
|
}
|
||||||
|
_play = false;
|
||||||
// 释放资源
|
_currentTask = null;
|
||||||
void dispose() {
|
_scheduleNextTask(delay: delay);
|
||||||
_dis = true;
|
if (delay == Duration.zero) {
|
||||||
_play = false;
|
Future.microtask(_drainPreloadQueue);
|
||||||
_tq.clear();
|
} else {
|
||||||
_rgc?.stop();
|
Future.delayed(delay, _drainPreloadQueue);
|
||||||
_rgc?.dispose();
|
}
|
||||||
_rgc = null;
|
}
|
||||||
_rsc?.stop();
|
|
||||||
_rsc?.dispose();
|
// 绑定控制器
|
||||||
_rsc = null;
|
void bindVapCtrl(VapController vapController) {
|
||||||
}
|
_mute = DataPersistence.getPlayGiftMusic();
|
||||||
|
_dis = false;
|
||||||
// 清除所有任务
|
_rgc = vapController;
|
||||||
void clearTasks() {
|
_log(
|
||||||
_play = false;
|
'bindVapCtrl hasVapCtrl=${_rgc != null} '
|
||||||
_tq.clear();
|
'hasSvgaCtrl=${_rsc != null} queue=${_tq.length} mute=$_mute',
|
||||||
_pause = false;
|
);
|
||||||
}
|
_rgc?.setAnimListener(
|
||||||
}
|
onVideoStart: () {
|
||||||
|
_log('vap onVideoStart path=${_currentTask?.path}');
|
||||||
// 任务模型
|
},
|
||||||
// 1. 修改 SCVapTask 类,实现 Comparable
|
onVideoComplete: () {
|
||||||
|
_log('vap onVideoComplete path=${_currentTask?.path}');
|
||||||
class SCVapTask implements Comparable<SCVapTask> {
|
_hcs();
|
||||||
final String path;
|
},
|
||||||
final int type;
|
onFailed: (code, type, msg) {
|
||||||
final int priority;
|
_log(
|
||||||
final Map<String, VAPContent>? customResources;
|
'vap onFailed path=${_currentTask?.path} code=$code type=$type msg=$msg',
|
||||||
final int _seq;
|
);
|
||||||
|
_hcs();
|
||||||
static int _nextSeq = 0;
|
},
|
||||||
|
);
|
||||||
SCVapTask({
|
_scheduleNextTask();
|
||||||
required this.path,
|
_drainPreloadQueue();
|
||||||
this.priority = 0,
|
}
|
||||||
this.customResources,
|
|
||||||
this.type = 0,
|
void bindSvgaCtrl(SVGAAnimationController svgaController) {
|
||||||
}) : _seq = _nextSeq++;
|
_dis = false;
|
||||||
|
_rsc = svgaController;
|
||||||
@override
|
_log(
|
||||||
int compareTo(SCVapTask other) {
|
'bindSvgaCtrl hasSvgaCtrl=${_rsc != null} '
|
||||||
// 先按优先级降序排列
|
'hasVapCtrl=${_rgc != null} queue=${_tq.length}',
|
||||||
int priorityComparison = other.priority.compareTo(priority);
|
);
|
||||||
if (priorityComparison != 0) {
|
_rsc?.addStatusListener((AnimationStatus status) {
|
||||||
return priorityComparison;
|
if (status.isCompleted) {
|
||||||
}
|
_log('svga completed path=${_currentTask?.path}');
|
||||||
// 相同优先级时,按序列号升序排列(先添加的先执行)
|
_rsc?.reset();
|
||||||
return _seq.compareTo(other._seq);
|
_play = false;
|
||||||
}
|
_currentTask = null;
|
||||||
|
_pn();
|
||||||
@override
|
}
|
||||||
String toString() {
|
});
|
||||||
return 'SCVapTask{path: $path, priority: $priority, seq: $_seq}';
|
_scheduleNextTask();
|
||||||
}
|
_drainPreloadQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优先队列实现
|
// 播放任务
|
||||||
class SCPriorityQueue<E> {
|
void play(
|
||||||
final List<E> _els = [];
|
String path, {
|
||||||
final Comparator<E> _cmp;
|
int priority = 0,
|
||||||
|
Map<String, VAPContent>? customResources,
|
||||||
SCPriorityQueue(this._cmp);
|
int type = 0,
|
||||||
|
}) {
|
||||||
void add(E element) {
|
if (path.isEmpty) {
|
||||||
_els.add(element);
|
_log('play ignored because path is empty');
|
||||||
_els.sort(_cmp);
|
return;
|
||||||
}
|
}
|
||||||
|
_log(
|
||||||
E removeFirst() {
|
'play request path=$path ext=${SCPathUtils.getFileExtension(path)} '
|
||||||
if (isEmpty) throw StateError("No elements");
|
'priority=$priority type=$type '
|
||||||
return _els.removeAt(0);
|
'sdkInt=${SCGlobalConfig.sdkInt} maxSdkNoAnim=${SCGlobalConfig.maxSdkNoAnim} '
|
||||||
}
|
'disposed=$_dis playing=$_play queueBefore=${_tq.length} '
|
||||||
|
'hasSvgaCtrl=${_rsc != null} hasVapCtrl=${_rgc != null}',
|
||||||
E? get first => isEmpty ? null : _els.first;
|
);
|
||||||
|
if (SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim) {
|
||||||
bool get isEmpty => _els.isEmpty;
|
if (_dis) {
|
||||||
|
_log('play ignored because manager is disposed path=$path');
|
||||||
bool get isNotEmpty => _els.isNotEmpty;
|
return;
|
||||||
|
}
|
||||||
int get length => _els.length;
|
final task = SCVapTask(
|
||||||
|
path: path,
|
||||||
void clear() => _els.clear();
|
priority: priority,
|
||||||
|
customResources: customResources,
|
||||||
List<E> get unorderedElements => List.from(_els);
|
);
|
||||||
|
|
||||||
// 实现 Iterable 接口
|
_tq.add(task);
|
||||||
Iterator<E> get iterator => _els.iterator;
|
_log('task enqueued path=$path queueAfter=${_tq.length}');
|
||||||
}
|
if (!_play) {
|
||||||
|
_pn();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_log(
|
||||||
|
'play ignored because sdkInt <= maxSdkNoAnim '
|
||||||
|
'sdkInt=${SCGlobalConfig.sdkInt} maxSdkNoAnim=${SCGlobalConfig.maxSdkNoAnim}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放下一个任务
|
||||||
|
Future<void> _pn() async {
|
||||||
|
if (_pause) {
|
||||||
|
_log('skip _pn because paused queue=${_tq.length}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_dis || _tq.isEmpty || _play) return;
|
||||||
|
|
||||||
|
final task = _tq.first;
|
||||||
|
if (task == null || !_isControllerReady(task)) {
|
||||||
|
_log(
|
||||||
|
'controller not ready for path=${task?.path} '
|
||||||
|
'needSvga=${task != null ? _needsSvgaController(task.path) : "unknown"} '
|
||||||
|
'hasSvgaCtrl=${_rsc != null} hasVapCtrl=${_rgc != null} '
|
||||||
|
'queue=${_tq.length}',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tq.removeFirst();
|
||||||
|
_play = true;
|
||||||
|
_currentTask = task;
|
||||||
|
try {
|
||||||
|
final pathType = SCPathUtils.getPathType(task.path);
|
||||||
|
_log(
|
||||||
|
'start task path=${task.path} '
|
||||||
|
'pathType=$pathType '
|
||||||
|
'queueRemaining=${_tq.length} '
|
||||||
|
'needSvga=${_needsSvgaController(task.path)}',
|
||||||
|
);
|
||||||
|
if (pathType == PathType.asset) {
|
||||||
|
await _pa(task);
|
||||||
|
} else if (pathType == PathType.file) {
|
||||||
|
await _pf(task);
|
||||||
|
} else if (pathType == PathType.network) {
|
||||||
|
await _pnw(task);
|
||||||
|
} else {
|
||||||
|
debugPrint('VAP_SVGA不支持的路径类型: ${task.path}');
|
||||||
|
_finishCurrentTask();
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
debugPrint('VAP_SVGA播放失败: $e\n$s');
|
||||||
|
_finishCurrentTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放资源文件
|
||||||
|
Future<void> _pa(SCVapTask task) async {
|
||||||
|
if (_dis) return;
|
||||||
|
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
|
||||||
|
_log('play asset svga path=${task.path}');
|
||||||
|
final entity = await _loadSvgaEntity(task.path);
|
||||||
|
_log('use prepared asset svga path=${task.path}');
|
||||||
|
_rsc?.videoItem = entity;
|
||||||
|
_rsc?.reset();
|
||||||
|
_rsc?.forward();
|
||||||
|
_log('forward asset svga path=${task.path}');
|
||||||
|
} else {
|
||||||
|
_log('play asset vap/mp4 path=${task.path}');
|
||||||
|
if (task.customResources != null) {
|
||||||
|
task.customResources?.forEach((k, v) async {
|
||||||
|
await _rgc?.setVapTagContent(k, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await _rgc?.playAsset(task.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放本地文件
|
||||||
|
Future<void> _pf(SCVapTask task) async {
|
||||||
|
if (_dis) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
|
||||||
|
final entity = await _loadSvgaEntity(task.path);
|
||||||
|
_log('play local svga file path=${task.path}');
|
||||||
|
_rsc?.videoItem = entity;
|
||||||
|
_rsc?.reset();
|
||||||
|
_rsc?.forward();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_rgc == null) {
|
||||||
|
_log('skip playFile because vap controller is null path=${task.path}');
|
||||||
|
_finishCurrentTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_log('play local vap/mp4 file path=${task.path}');
|
||||||
|
await _rgc?.setMute(_mute);
|
||||||
|
if (task.customResources != null) {
|
||||||
|
task.customResources?.forEach((k, v) async {
|
||||||
|
await _rgc?.setVapTagContent(k, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await _rgc!.playFile(task.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放网络资源
|
||||||
|
Future<void> _pnw(SCVapTask task) async {
|
||||||
|
if (_dis) return;
|
||||||
|
if (SCPathUtils.getFileExtension(task.path).toLowerCase() == ".svga") {
|
||||||
|
_log('play network svga path=${task.path}');
|
||||||
|
late final MovieEntity entity;
|
||||||
|
try {
|
||||||
|
entity = await _loadSvgaEntity(task.path);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('svga解析出错:$e');
|
||||||
|
_finishCurrentTask();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_log('use prepared network svga path=${task.path}');
|
||||||
|
_rsc?.videoItem = entity;
|
||||||
|
_rsc?.reset();
|
||||||
|
_rsc?.forward();
|
||||||
|
_log('forward network svga path=${task.path}');
|
||||||
|
} else {
|
||||||
|
_log('download network vap/mp4 path=${task.path}');
|
||||||
|
final playablePath = await _ensurePlayableFilePath(task.path);
|
||||||
|
if (!_dis) {
|
||||||
|
_log('use prepared network vap/mp4 local path=$playablePath');
|
||||||
|
await _pf(
|
||||||
|
SCVapTask(
|
||||||
|
path: playablePath,
|
||||||
|
priority: task.priority,
|
||||||
|
customResources: task.customResources,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理控制器状态变化
|
||||||
|
void _hcs() {
|
||||||
|
if (_rgc != null && !_dis) {
|
||||||
|
_log('finish vap task path=${_currentTask?.path}');
|
||||||
|
_finishCurrentTask(delay: const Duration(milliseconds: 50));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//暂停动画播放
|
||||||
|
void pauseAnim() {
|
||||||
|
_pause = true;
|
||||||
|
_log('pauseAnim queue=${_tq.length}');
|
||||||
|
}
|
||||||
|
|
||||||
|
//恢复动画播放
|
||||||
|
void resumeAnim() {
|
||||||
|
_pause = false;
|
||||||
|
_log('resumeAnim queue=${_tq.length}');
|
||||||
|
_pn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放资源
|
||||||
|
void dispose() {
|
||||||
|
_log('dispose queue=${_tq.length} currentPath=${_currentTask?.path}');
|
||||||
|
_dis = true;
|
||||||
|
_play = false;
|
||||||
|
_currentTask = null;
|
||||||
|
_tq.clear();
|
||||||
|
_preloadQueue.clear();
|
||||||
|
_queuedPreloadPaths.clear();
|
||||||
|
_activePreloadCount = 0;
|
||||||
|
_rgc?.stop();
|
||||||
|
_rgc?.dispose();
|
||||||
|
_rgc = null;
|
||||||
|
_rsc?.stop();
|
||||||
|
_rsc?.dispose();
|
||||||
|
_rsc = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除所有任务
|
||||||
|
void clearTasks() {
|
||||||
|
_log(
|
||||||
|
'clearTasks queueBefore=${_tq.length} currentPath=${_currentTask?.path}',
|
||||||
|
);
|
||||||
|
_play = false;
|
||||||
|
_currentTask = null;
|
||||||
|
_tq.clear();
|
||||||
|
_preloadQueue.clear();
|
||||||
|
_queuedPreloadPaths.clear();
|
||||||
|
_activePreloadCount = 0;
|
||||||
|
_pause = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务模型
|
||||||
|
// 1. 修改 SCVapTask 类,实现 Comparable
|
||||||
|
|
||||||
|
class SCVapTask implements Comparable<SCVapTask> {
|
||||||
|
final String path;
|
||||||
|
final int type;
|
||||||
|
final int priority;
|
||||||
|
final Map<String, VAPContent>? customResources;
|
||||||
|
final int _seq;
|
||||||
|
|
||||||
|
static int _nextSeq = 0;
|
||||||
|
|
||||||
|
SCVapTask({
|
||||||
|
required this.path,
|
||||||
|
this.priority = 0,
|
||||||
|
this.customResources,
|
||||||
|
this.type = 0,
|
||||||
|
}) : _seq = _nextSeq++;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int compareTo(SCVapTask other) {
|
||||||
|
// 先按优先级降序排列
|
||||||
|
int priorityComparison = other.priority.compareTo(priority);
|
||||||
|
if (priorityComparison != 0) {
|
||||||
|
return priorityComparison;
|
||||||
|
}
|
||||||
|
// 相同优先级时,按序列号升序排列(先添加的先执行)
|
||||||
|
return _seq.compareTo(other._seq);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SCVapTask{path: $path, priority: $priority, seq: $_seq}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先队列实现
|
||||||
|
class SCPriorityQueue<E> {
|
||||||
|
final List<E> _els = [];
|
||||||
|
final Comparator<E> _cmp;
|
||||||
|
|
||||||
|
SCPriorityQueue(this._cmp);
|
||||||
|
|
||||||
|
void add(E element) {
|
||||||
|
_els.add(element);
|
||||||
|
_els.sort(_cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
E removeFirst() {
|
||||||
|
if (isEmpty) throw StateError("No elements");
|
||||||
|
return _els.removeAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
E? get first => isEmpty ? null : _els.first;
|
||||||
|
|
||||||
|
bool get isEmpty => _els.isEmpty;
|
||||||
|
|
||||||
|
bool get isNotEmpty => _els.isNotEmpty;
|
||||||
|
|
||||||
|
int get length => _els.length;
|
||||||
|
|
||||||
|
void clear() => _els.clear();
|
||||||
|
|
||||||
|
List<E> get unorderedElements => List.from(_els);
|
||||||
|
|
||||||
|
// 实现 Iterable 接口
|
||||||
|
Iterator<E> get iterator => _els.iterator;
|
||||||
|
}
|
||||||
|
|||||||
@ -52,6 +52,7 @@ class SCPathUtils {
|
|||||||
'.gif',
|
'.gif',
|
||||||
'.mp4',
|
'.mp4',
|
||||||
'.vap',
|
'.vap',
|
||||||
|
'.svga',
|
||||||
].contains(ext);
|
].contains(ext);
|
||||||
|
|
||||||
return isDirectAsset && isSupportedAsset;
|
return isDirectAsset && isSupportedAsset;
|
||||||
@ -74,6 +75,7 @@ class SCPathUtils {
|
|||||||
'.mp4',
|
'.mp4',
|
||||||
'.mov',
|
'.mov',
|
||||||
'.avi',
|
'.avi',
|
||||||
|
'.svga',
|
||||||
'.png',
|
'.png',
|
||||||
'.jpg',
|
'.jpg',
|
||||||
'.txt',
|
'.txt',
|
||||||
@ -132,7 +134,6 @@ class SCPathUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool fileTypeIsPic(String filePath) {
|
static bool fileTypeIsPic(String filePath) {
|
||||||
String extension = path.extension(filePath).toLowerCase();
|
String extension = path.extension(filePath).toLowerCase();
|
||||||
|
|
||||||
|
|||||||
146
lib/shared/tools/sc_room_profile_cache.dart
Normal file
146
lib/shared/tools/sc_room_profile_cache.dart
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/follow_room_res.dart'
|
||||||
|
as follow;
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/join_room_res.dart'
|
||||||
|
as join;
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/my_room_res.dart';
|
||||||
|
import 'package:yumi/shared/business_logic/models/res/room_res.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
|
||||||
|
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||||||
|
|
||||||
|
class SCRoomProfileCache {
|
||||||
|
static const String _prefix = "room_profile_override";
|
||||||
|
|
||||||
|
static String _key(String roomId) {
|
||||||
|
final userId =
|
||||||
|
AccountStorage().getCurrentUser()?.userProfile?.id ?? "guest";
|
||||||
|
return "${_prefix}_${userId}_$roomId";
|
||||||
|
}
|
||||||
|
|
||||||
|
static String? preferNonEmpty(String? primary, String? fallback) {
|
||||||
|
if ((primary ?? "").trim().isNotEmpty) {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
if ((fallback ?? "").trim().isNotEmpty) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
return primary ?? fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String? preferCachedValue(String? cached, String? remote) {
|
||||||
|
if ((cached ?? "").trim().isNotEmpty) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
return preferNonEmpty(remote, cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, String> getRoomProfile(String roomId) {
|
||||||
|
if (roomId.trim().isEmpty) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
final raw = DataPersistence.getString(_key(roomId));
|
||||||
|
if (raw.isEmpty) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final data = jsonDecode(raw);
|
||||||
|
if (data is Map) {
|
||||||
|
return data.map(
|
||||||
|
(key, value) => MapEntry(key.toString(), value?.toString() ?? ""),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> saveRoomProfile({
|
||||||
|
required String roomId,
|
||||||
|
String? roomCover,
|
||||||
|
String? roomName,
|
||||||
|
String? roomDesc,
|
||||||
|
}) async {
|
||||||
|
if (roomId.trim().isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final current = getRoomProfile(roomId);
|
||||||
|
final payload = <String, String>{
|
||||||
|
"roomCover": preferNonEmpty(roomCover, current["roomCover"]) ?? "",
|
||||||
|
"roomName": preferNonEmpty(roomName, current["roomName"]) ?? "",
|
||||||
|
"roomDesc": preferNonEmpty(roomDesc, current["roomDesc"]) ?? "",
|
||||||
|
};
|
||||||
|
await DataPersistence.setString(_key(roomId), jsonEncode(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
static SocialChatRoomRes applyToSocialChatRoomRes(SocialChatRoomRes room) {
|
||||||
|
final cache = getRoomProfile(room.id ?? "");
|
||||||
|
if (cache.isEmpty) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
return room.copyWith(
|
||||||
|
roomCover: preferCachedValue(cache["roomCover"], room.roomCover),
|
||||||
|
roomName: preferCachedValue(cache["roomName"], room.roomName),
|
||||||
|
roomDesc: preferCachedValue(cache["roomDesc"], room.roomDesc),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static follow.FollowRoomRes applyToFollowRoomRes(follow.FollowRoomRes room) {
|
||||||
|
final roomProfile = room.roomProfile;
|
||||||
|
if (roomProfile == null) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
final cache = getRoomProfile(roomProfile.id ?? "");
|
||||||
|
if (cache.isEmpty) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
return room.copyWith(
|
||||||
|
roomProfile: roomProfile.copyWith(
|
||||||
|
roomCover: preferCachedValue(cache["roomCover"], roomProfile.roomCover),
|
||||||
|
roomName: preferCachedValue(cache["roomName"], roomProfile.roomName),
|
||||||
|
roomDesc: preferCachedValue(cache["roomDesc"], roomProfile.roomDesc),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MyRoomRes applyToMyRoomRes(MyRoomRes room) {
|
||||||
|
final cache = getRoomProfile(room.id ?? "");
|
||||||
|
if (cache.isEmpty) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
return room.copyWith(
|
||||||
|
roomCover: preferCachedValue(cache["roomCover"], room.roomCover),
|
||||||
|
roomName: preferCachedValue(cache["roomName"], room.roomName),
|
||||||
|
roomDesc: preferCachedValue(cache["roomDesc"], room.roomDesc),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static join.JoinRoomRes applyToJoinRoomRes(join.JoinRoomRes room) {
|
||||||
|
final roomProfile = room.roomProfile;
|
||||||
|
final basicRoomProfile = roomProfile?.roomProfile;
|
||||||
|
if (roomProfile == null || basicRoomProfile == null) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
final cache = getRoomProfile(basicRoomProfile.id ?? "");
|
||||||
|
if (cache.isEmpty) {
|
||||||
|
return room;
|
||||||
|
}
|
||||||
|
return room.copyWith(
|
||||||
|
roomProfile: roomProfile.copyWith(
|
||||||
|
roomProfile: basicRoomProfile.copyWith(
|
||||||
|
roomCover: preferCachedValue(
|
||||||
|
cache["roomCover"],
|
||||||
|
basicRoomProfile.roomCover,
|
||||||
|
),
|
||||||
|
roomName: preferCachedValue(
|
||||||
|
cache["roomName"],
|
||||||
|
basicRoomProfile.roomName,
|
||||||
|
),
|
||||||
|
roomDesc: preferCachedValue(
|
||||||
|
cache["roomDesc"],
|
||||||
|
basicRoomProfile.roomDesc,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,9 +11,18 @@ 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';
|
||||||
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
import 'package:yumi/shared/tools/sc_path_utils.dart';
|
||||||
|
import 'package:yumi/shared/tools/sc_room_profile_cache.dart';
|
||||||
|
|
||||||
import '../../shared/data_sources/models/enum/sc_room_roles_type.dart';
|
import '../../shared/data_sources/models/enum/sc_room_roles_type.dart';
|
||||||
|
|
||||||
|
const String kRoomCoverDefaultImg = "sc_images/general/sc_no_data.png";
|
||||||
|
|
||||||
|
String resolveRoomCoverUrl(String? roomId, String? roomCover) {
|
||||||
|
final cache = SCRoomProfileCache.getRoomProfile(roomId ?? "");
|
||||||
|
return SCRoomProfileCache.preferCachedValue(cache["roomCover"], roomCover) ??
|
||||||
|
"";
|
||||||
|
}
|
||||||
|
|
||||||
Widget head({
|
Widget head({
|
||||||
required String url,
|
required String url,
|
||||||
required double width,
|
required double width,
|
||||||
@ -26,52 +35,99 @@ Widget head({
|
|||||||
bool showDefault = true,
|
bool showDefault = true,
|
||||||
bool isRoom = false,
|
bool isRoom = false,
|
||||||
}) {
|
}) {
|
||||||
|
final bool hasPictureHeaddress =
|
||||||
|
headdress != null &&
|
||||||
|
headdress.isNotEmpty &&
|
||||||
|
SCPathUtils.fileTypeIsPic2(headdress);
|
||||||
|
final double avatarScale =
|
||||||
|
hasPictureHeaddress
|
||||||
|
? 0.64
|
||||||
|
: (headdress != null && headdress.isNotEmpty ? 0.72 : 1);
|
||||||
|
final double avatarWidth = width * avatarScale;
|
||||||
|
final double avatarHeight = (height ?? width) * avatarScale;
|
||||||
|
|
||||||
|
Widget avatar = SizedBox(
|
||||||
|
width: avatarWidth,
|
||||||
|
height: avatarHeight,
|
||||||
|
child: netImage(
|
||||||
|
url: url,
|
||||||
|
width: avatarWidth,
|
||||||
|
height: avatarHeight,
|
||||||
|
fit: fit,
|
||||||
|
noDefaultImg: !showDefault,
|
||||||
|
defaultImg: "sc_images/general/sc_icon_avar_defalt.png",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (shape == BoxShape.circle) {
|
||||||
|
avatar = ClipOval(child: avatar);
|
||||||
|
if (border != null) {
|
||||||
|
avatar = Container(
|
||||||
|
width: avatarWidth,
|
||||||
|
height: avatarHeight,
|
||||||
|
decoration: ShapeDecoration(shape: CircleBorder(side: border.top)),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: avatar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (borderRadius != null) {
|
||||||
|
avatar = ClipRRect(borderRadius: borderRadius, child: avatar);
|
||||||
|
if (border != null) {
|
||||||
|
avatar = Container(
|
||||||
|
width: avatarWidth,
|
||||||
|
height: avatarHeight,
|
||||||
|
decoration: BoxDecoration(border: border, borderRadius: borderRadius),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: avatar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (border != null) {
|
||||||
|
avatar = Container(
|
||||||
|
width: avatarWidth,
|
||||||
|
height: avatarHeight,
|
||||||
|
decoration: BoxDecoration(border: border),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: avatar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return RepaintBoundary(
|
return RepaintBoundary(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
avatar,
|
||||||
padding: EdgeInsets.all(width * 0.12),
|
hasPictureHeaddress
|
||||||
width: width,
|
? ColorFiltered(
|
||||||
height: height ?? width,
|
colorFilter: const ColorFilter.matrix([
|
||||||
child: ExtendedImage.network(
|
1,
|
||||||
url,
|
0,
|
||||||
width: width,
|
0,
|
||||||
height: height ?? width,
|
0,
|
||||||
fit: fit,
|
0,
|
||||||
cache: true,
|
0,
|
||||||
shape: shape,
|
1,
|
||||||
border: border,
|
0,
|
||||||
clearMemoryCacheWhenDispose: false,
|
0,
|
||||||
clearMemoryCacheIfFailed: true,
|
0,
|
||||||
borderRadius: borderRadius,
|
0,
|
||||||
loadStateChanged: (ExtendedImageState state) {
|
0,
|
||||||
if (state.extendedImageLoadState == LoadState.completed) {
|
1,
|
||||||
return ExtendedRawImage(
|
0,
|
||||||
image: state.extendedImageInfo?.image,
|
0,
|
||||||
fit: fit,
|
-1,
|
||||||
);
|
-1,
|
||||||
} else if (state.extendedImageLoadState == LoadState.loading) {
|
-1,
|
||||||
if (showDefault) {
|
3,
|
||||||
return ExtendedImage.asset(
|
0,
|
||||||
shape: shape,
|
]),
|
||||||
"sc_images/general/sc_icon_loading.png",
|
child: netImage(
|
||||||
fit: BoxFit.cover,
|
url: headdress,
|
||||||
);
|
width: width,
|
||||||
}
|
height: height ?? width,
|
||||||
return Container();
|
fit: BoxFit.contain,
|
||||||
} else {
|
noDefaultImg: true,
|
||||||
return ExtendedImage.asset(
|
),
|
||||||
shape: shape,
|
)
|
||||||
"sc_images/general/sc_icon_avar_defalt.png",
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
headdress != null && SCPathUtils.fileTypeIsPic2(headdress)
|
|
||||||
? netImage(url: headdress, width: width, height: width)
|
|
||||||
: Container(),
|
: Container(),
|
||||||
headdress != null &&
|
headdress != null &&
|
||||||
SCPathUtils.getFileExtension(headdress).toLowerCase() ==
|
SCPathUtils.getFileExtension(headdress).toLowerCase() ==
|
||||||
@ -86,7 +142,8 @@ Widget head({
|
|||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
headdress != null &&
|
headdress != null &&
|
||||||
SCPathUtils.getFileExtension(headdress).toLowerCase() == ".mp4" &&
|
SCPathUtils.getFileExtension(headdress).toLowerCase() ==
|
||||||
|
".mp4" &&
|
||||||
SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim
|
SCGlobalConfig.sdkInt > SCGlobalConfig.maxSdkNoAnim
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
width: width,
|
width: width,
|
||||||
@ -301,7 +358,7 @@ searchWidget({
|
|||||||
"sc_images/index/sc_icon_serach2.png",
|
"sc_images/index/sc_icon_serach2.png",
|
||||||
width: 20.w,
|
width: 20.w,
|
||||||
color: serachIconColor ?? Color(0xff18F2B1),
|
color: serachIconColor ?? Color(0xff18F2B1),
|
||||||
height: 20.w
|
height: 20.w,
|
||||||
),
|
),
|
||||||
SizedBox(width: width(6)),
|
SizedBox(width: width(6)),
|
||||||
_buildPhoneInput(
|
_buildPhoneInput(
|
||||||
@ -343,7 +400,10 @@ _buildPhoneInput(
|
|||||||
cursorColor: SocialChatTheme.primaryColor,
|
cursorColor: SocialChatTheme.primaryColor,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: tint,
|
hintText: tint,
|
||||||
hintStyle: TextStyle(color: SocialChatTheme.textSecondary, fontSize: hintSize ?? sp(14)),
|
hintStyle: TextStyle(
|
||||||
|
color: SocialChatTheme.textSecondary,
|
||||||
|
fontSize: hintSize ?? sp(14),
|
||||||
|
),
|
||||||
counterText: '',
|
counterText: '',
|
||||||
isDense: true,
|
isDense: true,
|
||||||
filled: false,
|
filled: false,
|
||||||
|
|||||||
@ -12,211 +12,215 @@ import 'package:yumi/shared/business_logic/models/res/join_room_res.dart';
|
|||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/app/constants/sc_screen.dart';
|
import 'package:yumi/app/constants/sc_screen.dart';
|
||||||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||||||
|
|
||||||
// 默认位置
|
// 默认位置
|
||||||
Offset kDefaultFloatOffset = Offset(
|
Offset kDefaultFloatOffset = Offset(
|
||||||
ScreenUtil().screenWidth - width(90),
|
ScreenUtil().screenWidth - width(90),
|
||||||
ScreenUtil().screenHeight - height(120),
|
ScreenUtil().screenHeight - height(120),
|
||||||
);
|
);
|
||||||
|
|
||||||
class SCFloatIchart {
|
class SCFloatIchart {
|
||||||
/// 单例模式
|
/// 单例模式
|
||||||
static final SCFloatIchart _SCFloatIchart = SCFloatIchart._internal(); //1
|
static final SCFloatIchart _SCFloatIchart = SCFloatIchart._internal(); //1
|
||||||
factory SCFloatIchart() {
|
factory SCFloatIchart() {
|
||||||
return _SCFloatIchart;
|
return _SCFloatIchart;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCFloatIchart._internal();
|
SCFloatIchart._internal();
|
||||||
|
|
||||||
bool _inserted = false;
|
bool _inserted = false;
|
||||||
OverlayEntry? overlayEntry;
|
OverlayEntry? overlayEntry;
|
||||||
BuildContext? context;
|
BuildContext? context;
|
||||||
Offset offset = kDefaultFloatOffset;
|
Offset offset = kDefaultFloatOffset;
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
if (overlayEntry != null) {
|
if (overlayEntry != null) {
|
||||||
remove(); // 先移除已有的
|
remove(); // 先移除已有的
|
||||||
}
|
}
|
||||||
var overlayState = Overlay.of(context!);
|
var overlayState = Overlay.of(context!);
|
||||||
overlayEntry = OverlayEntry(
|
overlayEntry = OverlayEntry(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
if (SCNavigatorUtils.inLoginPage) {
|
if (SCNavigatorUtils.inLoginPage) {
|
||||||
remove();
|
remove();
|
||||||
return Container();
|
return Container();
|
||||||
} else {
|
} else {
|
||||||
return buildToastLayout();
|
return buildToastLayout();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
overlayState.insert(overlayEntry!);
|
overlayState.insert(overlayEntry!);
|
||||||
_inserted = true; // 标记已插入
|
_inserted = true; // 标记已插入
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isShow() {
|
bool isShow() {
|
||||||
return _inserted;
|
return _inserted;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove() {
|
||||||
if (overlayEntry != null && overlayEntry!.mounted) {
|
if (overlayEntry != null && overlayEntry!.mounted) {
|
||||||
if (_inserted) {
|
if (_inserted) {
|
||||||
overlayEntry?.remove();
|
overlayEntry?.remove();
|
||||||
_inserted = false;
|
_inserted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutBuilder buildToastLayout() {
|
LayoutBuilder buildToastLayout() {
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Positioned(
|
Positioned(
|
||||||
left: offset.dx,
|
left: offset.dx,
|
||||||
top: offset.dy,
|
top: offset.dy,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
//更新child的位置
|
//更新child的位置
|
||||||
onPanUpdate: (details) {
|
onPanUpdate: (details) {
|
||||||
var localPosition = details.delta;
|
var localPosition = details.delta;
|
||||||
var dx = (offset.dx + localPosition.dx).clamp(
|
var dx = (offset.dx + localPosition.dx).clamp(
|
||||||
0.0,
|
0.0,
|
||||||
ScreenUtil().screenWidth - width(90),
|
ScreenUtil().screenWidth - width(90),
|
||||||
);
|
);
|
||||||
var dy = (offset.dy + localPosition.dy).clamp(
|
var dy = (offset.dy + localPosition.dy).clamp(
|
||||||
0.0,
|
0.0,
|
||||||
ScreenUtil().screenHeight - height(55) - kToolbarHeight,
|
ScreenUtil().screenHeight - height(55) - kToolbarHeight,
|
||||||
);
|
);
|
||||||
offset = Offset(dx, dy);
|
offset = Offset(dx, dy);
|
||||||
overlayEntry!.markNeedsBuild();
|
overlayEntry!.markNeedsBuild();
|
||||||
},
|
},
|
||||||
//拖动结束,处理child贴边悬浮
|
//拖动结束,处理child贴边悬浮
|
||||||
onPanEnd: (details) {
|
onPanEnd: (details) {
|
||||||
var oldPosition = offset.dx;
|
var oldPosition = offset.dx;
|
||||||
var targets =
|
var targets =
|
||||||
offset.dx + width(80) > (ScreenUtil().screenWidth / 2)
|
offset.dx + width(80) > (ScreenUtil().screenWidth / 2)
|
||||||
? ScreenUtil().screenWidth - width(90)
|
? ScreenUtil().screenWidth - width(90)
|
||||||
: 0;
|
: 0;
|
||||||
timer = Timer.periodic(Duration(milliseconds: 1), (t) {
|
timer = Timer.periodic(Duration(milliseconds: 1), (t) {
|
||||||
if (targets > 0) {
|
if (targets > 0) {
|
||||||
oldPosition++;
|
oldPosition++;
|
||||||
} else {
|
} else {
|
||||||
oldPosition--;
|
oldPosition--;
|
||||||
}
|
}
|
||||||
offset = Offset(oldPosition.toDouble(), offset.dy);
|
offset = Offset(oldPosition.toDouble(), offset.dy);
|
||||||
overlayEntry!.markNeedsBuild();
|
overlayEntry!.markNeedsBuild();
|
||||||
if (oldPosition < 0 ||
|
if (oldPosition < 0 ||
|
||||||
oldPosition > ScreenUtil().screenWidth - width(90)) {
|
oldPosition > ScreenUtil().screenWidth - width(90)) {
|
||||||
timer?.cancel();
|
timer?.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
child: float(),
|
child: float(),
|
||||||
margin: EdgeInsets.only(bottom: height(kToolbarHeight)),
|
margin: EdgeInsets.only(bottom: height(kToolbarHeight)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget float() {
|
Widget float() {
|
||||||
JoinRoomRes? room =
|
JoinRoomRes? room =
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
context!,
|
context!,
|
||||||
listen: false,
|
listen: false,
|
||||||
).currenRoom;
|
).currenRoom;
|
||||||
// User user = Provider.of<RealTimeCommunicationManager>(context, listen: false).maiMap[0];
|
// User user = Provider.of<RealTimeCommunicationManager>(context, listen: false).maiMap[0];
|
||||||
SocialChatLoginRes? user = AccountStorage().getCurrentUser();
|
SocialChatLoginRes? user = AccountStorage().getCurrentUser();
|
||||||
return FloatRoomWindow(room: room, user: user, remove: remove);
|
return FloatRoomWindow(room: room, user: user, remove: remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(BuildContext context) {
|
void init(BuildContext context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FloatRoomWindow extends StatefulWidget {
|
class FloatRoomWindow extends StatefulWidget {
|
||||||
final JoinRoomRes? room;
|
final JoinRoomRes? room;
|
||||||
final SocialChatLoginRes? user;
|
final SocialChatLoginRes? user;
|
||||||
final Function remove;
|
final Function remove;
|
||||||
|
|
||||||
const FloatRoomWindow({Key? key, this.room, this.user, required this.remove})
|
const FloatRoomWindow({Key? key, this.room, this.user, required this.remove})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_FloatRoomWindowState createState() => _FloatRoomWindowState();
|
_FloatRoomWindowState createState() => _FloatRoomWindowState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FloatRoomWindowState extends State<FloatRoomWindow> {
|
class _FloatRoomWindowState extends State<FloatRoomWindow> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SCDebounceWidget(
|
return SCDebounceWidget(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
SCRoomUtils.openCurrentRoom(context);
|
SCRoomUtils.openCurrentRoom(context);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 55.w,
|
height: 55.w,
|
||||||
width: 90.w,
|
width: 90.w,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
color: SocialChatTheme.primaryLight,
|
color: SocialChatTheme.primaryLight,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SizedBox(width: 8.w),
|
SizedBox(width: 8.w),
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
netImage(
|
netImage(
|
||||||
url: widget.room?.roomProfile?.roomProfile?.roomCover ?? "",
|
url: resolveRoomCoverUrl(
|
||||||
width: 40.w,
|
widget.room?.roomProfile?.roomProfile?.id,
|
||||||
height: 40.w,
|
widget.room?.roomProfile?.roomProfile?.roomCover,
|
||||||
borderRadius: BorderRadius.circular(8.w),
|
),
|
||||||
),
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
Image.asset(
|
width: 40.w,
|
||||||
"sc_images/general/sc_icon_online_user.png",
|
height: 40.w,
|
||||||
width: 15.w,
|
borderRadius: BorderRadius.circular(8.w),
|
||||||
height: 15.w,
|
),
|
||||||
),
|
Image.asset(
|
||||||
],
|
"sc_images/general/sc_icon_online_user.png",
|
||||||
),
|
width: 15.w,
|
||||||
SizedBox(width: width(5)),
|
height: 15.w,
|
||||||
SCDebounceWidget(
|
),
|
||||||
onTap: () {
|
],
|
||||||
Provider.of<RealTimeCommunicationManager>(
|
),
|
||||||
context,
|
SizedBox(width: width(5)),
|
||||||
listen: false,
|
SCDebounceWidget(
|
||||||
).exitCurrentVoiceRoomSession(false);
|
onTap: () {
|
||||||
Timer(Duration(milliseconds: 550), () {
|
Provider.of<RealTimeCommunicationManager>(
|
||||||
widget.remove.call();
|
context,
|
||||||
});
|
listen: false,
|
||||||
},
|
).exitCurrentVoiceRoomSession(false);
|
||||||
child: Padding(
|
Timer(Duration(milliseconds: 550), () {
|
||||||
padding: EdgeInsets.all(5.w),
|
widget.remove.call();
|
||||||
child: Image.asset(
|
});
|
||||||
"sc_images/index/sc_icon_room_flot_close.png",
|
},
|
||||||
width: 20.w,
|
child: Padding(
|
||||||
height: 20.w,
|
padding: EdgeInsets.all(5.w),
|
||||||
),
|
child: Image.asset(
|
||||||
),
|
"sc_images/index/sc_icon_room_flot_close.png",
|
||||||
),
|
width: 20.w,
|
||||||
],
|
height: 20.w,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
],
|
||||||
}
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -17,10 +17,15 @@ class _VapPlusSvgaPlayerState extends State<VapPlusSvgaPlayer>
|
|||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late SVGAAnimationController _svgaController;
|
late SVGAAnimationController _svgaController;
|
||||||
|
|
||||||
|
void _giftFxLog(String message) {
|
||||||
|
debugPrint('[GiftFX][Widget] $message');
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_svgaController = SVGAAnimationController(vsync: this);
|
_svgaController = SVGAAnimationController(vsync: this);
|
||||||
|
_giftFxLog('initState tag=${widget.tag}');
|
||||||
if (widget.tag == "room_gift") {
|
if (widget.tag == "room_gift") {
|
||||||
SCGiftVapSvgaManager().bindSvgaCtrl(_svgaController);
|
SCGiftVapSvgaManager().bindSvgaCtrl(_svgaController);
|
||||||
}
|
}
|
||||||
@ -28,31 +33,43 @@ class _VapPlusSvgaPlayerState extends State<VapPlusSvgaPlayer>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_giftFxLog('dispose tag=${widget.tag}');
|
||||||
SCGiftVapSvgaManager().dispose();
|
SCGiftVapSvgaManager().dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return IgnorePointer(
|
||||||
children: [
|
child: RepaintBoundary(
|
||||||
Positioned.fill(
|
child: Stack(
|
||||||
child: IgnorePointer(
|
children: [
|
||||||
// VapView可以通过外层包Container(),设置宽高来限制弹出视频的宽高
|
Positioned.fill(
|
||||||
// VapView can set the width and height through the outer package Container() to limit the width and height of the pop-up video
|
child: RepaintBoundary(
|
||||||
child: VapView(
|
child: VapView(
|
||||||
scaleType: ScaleType.centerCrop,
|
scaleType: ScaleType.fitCenter,
|
||||||
onViewCreated: (controller) {
|
onViewCreated: (controller) {
|
||||||
if (widget.tag == "room_gift") {
|
_giftFxLog('onViewCreated tag=${widget.tag}');
|
||||||
SCGiftVapSvgaManager().bindVapCtrl(controller);
|
if (widget.tag == "room_gift") {
|
||||||
// VapManager().play("https://res.cloudinary.com/dkmchpua1/video/upload/v1737624783/vcg9co6yyfqsadgety1n.mp4");
|
SCGiftVapSvgaManager().bindVapCtrl(controller);
|
||||||
}
|
// VapManager().play("https://res.cloudinary.com/dkmchpua1/video/upload/v1737624783/vcg9co6yyfqsadgety1n.mp4");
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Positioned.fill(
|
||||||
|
child: RepaintBoundary(
|
||||||
|
child: SVGAImage(
|
||||||
|
_svgaController,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
allowDrawingOverflow: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Positioned.fill(child: SVGAImage(_svgaController, fit: BoxFit.fill)),
|
),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,7 +125,7 @@ class _FloatingGiftScreenWidgetState extends State<FloatingGiftScreenWidget>
|
|||||||
SCRoomUtils.goRoom(
|
SCRoomUtils.goRoom(
|
||||||
widget.message.roomId!,
|
widget.message.roomId!,
|
||||||
navigatorKey.currentState!.context,
|
navigatorKey.currentState!.context,
|
||||||
fromFloting: true
|
fromFloting: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -157,7 +157,7 @@ class _FloatingGiftScreenWidgetState extends State<FloatingGiftScreenWidget>
|
|||||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: AssetImage("sc_images/room/sc_icon_gift_flosc_bg.png"),
|
image: AssetImage("sc_images/room/sc_icon_gift_float_bg.png"),
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -278,18 +278,18 @@ class _FloatingGiftScreenWidgetState extends State<FloatingGiftScreenWidget>
|
|||||||
String _getLuckGiftFloatBg() {
|
String _getLuckGiftFloatBg() {
|
||||||
if ((widget.message.coins ?? 0) > 4999 &&
|
if ((widget.message.coins ?? 0) > 4999 &&
|
||||||
(widget.message.coins ?? 0) < 10000) {
|
(widget.message.coins ?? 0) < 10000) {
|
||||||
return "sc_images/room/sc_icon_luck_gift_flosc_bg1.png";
|
return "sc_images/room/sc_icon_luck_gift_float_bg1.png";
|
||||||
} else if ((widget.message.coins ?? 0) > 9999 &&
|
} else if ((widget.message.coins ?? 0) > 9999 &&
|
||||||
(widget.message.coins ?? 0) < 75000) {
|
(widget.message.coins ?? 0) < 75000) {
|
||||||
return "sc_images/room/sc_icon_luck_gift_flosc_bg2.png";
|
return "sc_images/room/sc_icon_luck_gift_float_bg2.png";
|
||||||
} else if ((widget.message.coins ?? 0) > 74999 &&
|
} else if ((widget.message.coins ?? 0) > 74999 &&
|
||||||
(widget.message.coins ?? 0) < 150000) {
|
(widget.message.coins ?? 0) < 150000) {
|
||||||
return "sc_images/room/sc_icon_luck_gift_flosc_bg3.png";
|
return "sc_images/room/sc_icon_luck_gift_float_bg3.png";
|
||||||
} else if ((widget.message.coins ?? 0) > 149999 &&
|
} else if ((widget.message.coins ?? 0) > 149999 &&
|
||||||
(widget.message.coins ?? 0) < 1500000) {
|
(widget.message.coins ?? 0) < 1500000) {
|
||||||
return "sc_images/room/sc_icon_luck_gift_flosc_bg4.png";
|
return "sc_images/room/sc_icon_luck_gift_float_bg4.png";
|
||||||
} else if ((widget.message.coins ?? 0) > 1499999) {
|
} else if ((widget.message.coins ?? 0) > 1499999) {
|
||||||
return "sc_images/room/sc_icon_luck_gift_flosc_bg5.png";
|
return "sc_images/room/sc_icon_luck_gift_float_bg5.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
@ -127,7 +127,7 @@ class _FloatingLuckGiftScreenWidgetState
|
|||||||
SCRoomUtils.goRoom(
|
SCRoomUtils.goRoom(
|
||||||
widget.message.roomId!,
|
widget.message.roomId!,
|
||||||
navigatorKey.currentState!.context,
|
navigatorKey.currentState!.context,
|
||||||
fromFloting: true
|
fromFloting: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -161,7 +161,7 @@ class _FloatingLuckGiftScreenWidgetState
|
|||||||
flipX: SCGlobalConfig.lang == "ar" ? true : false, // 水平翻转
|
flipX: SCGlobalConfig.lang == "ar" ? true : false, // 水平翻转
|
||||||
flipY: false, // 垂直翻转设为 false
|
flipY: false, // 垂直翻转设为 false
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
"sc_images/room/sc_icon_luck_gift_flosc_n_bg.png",
|
"sc_images/room/sc_icon_luck_gift_float_n_bg.png",
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -322,7 +322,7 @@ class _FloatingLuckGiftScreenWidgetState
|
|||||||
SCRoomUtils.goRoom(
|
SCRoomUtils.goRoom(
|
||||||
widget.message.roomId!,
|
widget.message.roomId!,
|
||||||
navigatorKey.currentState!.context,
|
navigatorKey.currentState!.context,
|
||||||
fromFloting: true
|
fromFloting: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -354,7 +354,7 @@ class _FloatingLuckGiftScreenWidgetState
|
|||||||
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: AssetImage("sc_images/room/sc_icon_gift_flosc_bg.png"),
|
image: AssetImage("sc_images/room/sc_icon_gift_float_bg.png"),
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -47,13 +47,19 @@ class _RoomHeadWidgetState extends State<RoomHeadWidget> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
netImage(
|
netImage(
|
||||||
url:
|
url: resolveRoomCoverUrl(
|
||||||
provider
|
provider
|
||||||
.currenRoom
|
.currenRoom
|
||||||
?.roomProfile
|
?.roomProfile
|
||||||
?.roomProfile
|
?.roomProfile
|
||||||
?.roomCover ??
|
?.id,
|
||||||
"",
|
provider
|
||||||
|
.currenRoom
|
||||||
|
?.roomProfile
|
||||||
|
?.roomProfile
|
||||||
|
?.roomCover,
|
||||||
|
),
|
||||||
|
defaultImg: kRoomCoverDefaultImg,
|
||||||
width: 28.w,
|
width: 28.w,
|
||||||
height: 28.w,
|
height: 28.w,
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
|
|||||||
@ -1,173 +1,176 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
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:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:yumi/app/constants/sc_global_config.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||||||
|
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||||||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
||||||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
import 'package:yumi/services/room/rc_room_manager.dart';
|
||||||
import 'package:yumi/shared/tools/sc_lk_dialog_util.dart';
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
||||||
import 'package:yumi/services/room/rc_room_manager.dart';
|
import 'package:yumi/modules/room/online/room_online_page.dart';
|
||||||
import 'package:yumi/services/audio/rtc_manager.dart';
|
import 'package:yumi/modules/room/rank/room_gift_rank_page.dart';
|
||||||
import 'package:yumi/modules/room/online/room_online_page.dart';
|
|
||||||
import 'package:yumi/modules/room/rank/room_gift_rank_page.dart';
|
class RoomOnlineUserWidget extends StatefulWidget {
|
||||||
|
const RoomOnlineUserWidget({super.key});
|
||||||
class RoomOnlineUserWidget extends StatefulWidget {
|
|
||||||
@override
|
@override
|
||||||
_RoomOnlineUserWidgetState createState() => _RoomOnlineUserWidgetState();
|
State<RoomOnlineUserWidget> createState() => _RoomOnlineUserWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RoomOnlineUserWidgetState extends State<RoomOnlineUserWidget> {
|
class _RoomOnlineUserWidgetState extends State<RoomOnlineUserWidget> {
|
||||||
@override
|
void _openRoomOnlinePage(RtcProvider ref) {
|
||||||
Widget build(BuildContext context) {
|
showBottomInBottomDialog(
|
||||||
return Consumer<RtcProvider>(
|
context,
|
||||||
builder: (context, ref, child) {
|
RoomOnlinePage(roomId: ref.currenRoom?.roomProfile?.roomProfile?.id),
|
||||||
return Row(
|
);
|
||||||
children: [
|
}
|
||||||
_buildExperience(),
|
|
||||||
SizedBox(width: 15.w),
|
@override
|
||||||
ref.onlineUsers.isNotEmpty
|
Widget build(BuildContext context) {
|
||||||
? Expanded(
|
return Consumer<RtcProvider>(
|
||||||
child: Row(
|
builder: (context, ref, child) {
|
||||||
mainAxisSize: MainAxisSize.min,
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Spacer(),
|
_buildExperience(),
|
||||||
SizedBox(
|
SizedBox(width: 15.w),
|
||||||
height: 25.w,
|
ref.onlineUsers.isNotEmpty
|
||||||
child: Stack(
|
? Expanded(
|
||||||
clipBehavior: Clip.none,
|
child: Row(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
SingleChildScrollView(
|
children: [
|
||||||
scrollDirection: Axis.horizontal,
|
Spacer(),
|
||||||
child: Row(
|
GestureDetector(
|
||||||
mainAxisSize: MainAxisSize.min,
|
behavior: HitTestBehavior.opaque,
|
||||||
children: List.generate(
|
onTap: () => _openRoomOnlinePage(ref),
|
||||||
ref.onlineUsers.length,
|
child: Row(
|
||||||
(index) {
|
mainAxisSize: MainAxisSize.min,
|
||||||
return Transform.translate(
|
children: [
|
||||||
offset: Offset(-3.w * index, 0),
|
SizedBox(
|
||||||
child: Padding(
|
height: 25.w,
|
||||||
padding: EdgeInsets.only(right: 0.w),
|
child: Stack(
|
||||||
child: netImage(
|
clipBehavior: Clip.none,
|
||||||
url:
|
children: [
|
||||||
ref
|
SingleChildScrollView(
|
||||||
.onlineUsers[index]
|
scrollDirection: Axis.horizontal,
|
||||||
.userAvatar ??
|
child: Row(
|
||||||
"",
|
mainAxisSize: MainAxisSize.min,
|
||||||
width: 23.w,
|
children: List.generate(
|
||||||
height: 23.w,
|
ref.onlineUsers.length,
|
||||||
shape: BoxShape.circle,
|
(index) {
|
||||||
border: Border.all(
|
return Transform.translate(
|
||||||
color: Colors.white,
|
offset: Offset(-3.w * index, 0),
|
||||||
width: 1.w,
|
child: Padding(
|
||||||
),
|
padding: EdgeInsets.only(
|
||||||
),
|
right: 0.w,
|
||||||
),
|
),
|
||||||
);
|
child: netImage(
|
||||||
},
|
url:
|
||||||
),
|
ref
|
||||||
),
|
.onlineUsers[index]
|
||||||
),
|
.userAvatar ??
|
||||||
],
|
"",
|
||||||
),
|
width: 23.w,
|
||||||
),
|
height: 23.w,
|
||||||
GestureDetector(
|
shape: BoxShape.circle,
|
||||||
child: Container(
|
border: Border.all(
|
||||||
padding: EdgeInsets.symmetric(
|
color: Colors.white,
|
||||||
horizontal: 3.w,
|
width: 1.w,
|
||||||
vertical: 3.w,
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
),
|
||||||
children: [
|
);
|
||||||
Image.asset(
|
},
|
||||||
"sc_images/room/sc_icon_online_peple.png",
|
),
|
||||||
width: 12.w,
|
),
|
||||||
height: 12.sp,
|
),
|
||||||
),
|
],
|
||||||
text(
|
),
|
||||||
"${ref.onlineUsers.length}",
|
),
|
||||||
fontSize: 9.sp,
|
Container(
|
||||||
),
|
padding: EdgeInsets.symmetric(
|
||||||
],
|
horizontal: 3.w,
|
||||||
),
|
vertical: 3.w,
|
||||||
),
|
),
|
||||||
onTap: () {
|
child: Column(
|
||||||
showBottomInBottomDialog(
|
children: [
|
||||||
context,
|
Image.asset(
|
||||||
RoomOnlinePage(
|
"sc_images/room/sc_icon_online_peple.png",
|
||||||
roomId:
|
width: 12.w,
|
||||||
ref
|
height: 12.sp,
|
||||||
.currenRoom
|
),
|
||||||
?.roomProfile
|
text(
|
||||||
?.roomProfile
|
"${ref.onlineUsers.length}",
|
||||||
?.id,
|
fontSize: 9.sp,
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 5.w,)
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
)
|
SizedBox(width: 5.w),
|
||||||
: Container(),
|
],
|
||||||
],
|
),
|
||||||
);
|
)
|
||||||
},
|
: Container(),
|
||||||
);
|
],
|
||||||
}
|
);
|
||||||
|
},
|
||||||
///房间榜单入口
|
);
|
||||||
_buildExperience() {
|
}
|
||||||
return Consumer<SocialChatRoomManager>(
|
|
||||||
builder: (context, ref, child) {
|
///房间榜单入口
|
||||||
return GestureDetector(
|
_buildExperience() {
|
||||||
child: Container(
|
return Consumer<SocialChatRoomManager>(
|
||||||
width: 90.w,
|
builder: (context, ref, child) {
|
||||||
height: 27.w,
|
return GestureDetector(
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
color: Colors.white10,
|
width: 90.w,
|
||||||
borderRadius: BorderRadiusDirectional.only(
|
height: 27.w,
|
||||||
topEnd: Radius.circular(20.w),
|
decoration: BoxDecoration(
|
||||||
bottomEnd: Radius.circular(20.w),
|
color: Colors.white10,
|
||||||
),
|
borderRadius: BorderRadiusDirectional.only(
|
||||||
),
|
topEnd: Radius.circular(20.w),
|
||||||
alignment: AlignmentDirectional.center,
|
bottomEnd: Radius.circular(20.w),
|
||||||
child: Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
),
|
||||||
children: [
|
alignment: AlignmentDirectional.center,
|
||||||
Image.asset(
|
child: Row(
|
||||||
"sc_images/room/sc_icon_room_contribute.png",
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
width: 18.w,
|
children: [
|
||||||
height: 18.w,
|
Image.asset(
|
||||||
),
|
"sc_images/room/sc_icon_room_contribute.png",
|
||||||
SizedBox(width: 5.w),
|
width: 18.w,
|
||||||
text(
|
height: 18.w,
|
||||||
"${ref.roomContributeLevelRes?.thisWeekIntegral ?? 0}",
|
),
|
||||||
fontSize: 13.sp,
|
SizedBox(width: 5.w),
|
||||||
textColor: Colors.orangeAccent,
|
text(
|
||||||
fontWeight: FontWeight.w600,
|
"${ref.roomContributeLevelRes?.thisWeekIntegral ?? 0}",
|
||||||
),
|
fontSize: 13.sp,
|
||||||
Icon(
|
textColor: Colors.orangeAccent,
|
||||||
Icons.chevron_right,
|
fontWeight: FontWeight.w600,
|
||||||
size: 13.w,
|
),
|
||||||
color: Colors.orangeAccent,
|
Icon(
|
||||||
),
|
Icons.chevron_right,
|
||||||
],
|
size: 13.w,
|
||||||
),
|
color: Colors.orangeAccent,
|
||||||
),
|
),
|
||||||
onTap: () {
|
],
|
||||||
SmartDialog.show(
|
),
|
||||||
tag: "showRoomGiftRankPage",
|
),
|
||||||
alignment: Alignment.bottomCenter,
|
onTap: () {
|
||||||
animationType: SmartAnimationType.fade,
|
SmartDialog.show(
|
||||||
builder: (_) {
|
tag: "showRoomGiftRankPage",
|
||||||
return RoomGiftRankPage();
|
alignment: Alignment.bottomCenter,
|
||||||
},
|
animationType: SmartAnimationType.fade,
|
||||||
);
|
builder: (_) {
|
||||||
},
|
return RoomGiftRankPage();
|
||||||
);
|
},
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
}
|
);
|
||||||
}
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
BIN
sc_images/general/sc_no_data.png
Normal file
BIN
sc_images/general/sc_no_data.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
46
需求进度.md
46
需求进度.md
@ -5,7 +5,20 @@
|
|||||||
|
|
||||||
## 已完成模块
|
## 已完成模块
|
||||||
- 创建并持续维护进度跟踪文件。
|
- 创建并持续维护进度跟踪文件。
|
||||||
|
- 已继续排查语言房 gift 动画链路:确认送礼后会同时走本地房间消息、滚屏礼物条和大额礼物全局飘屏三条路径,并修复动画管理器在控制器尚未绑定完成时提前消费队列导致后续动画不再播放的问题。
|
||||||
|
- 已定位并修复语言房礼物飘屏资源引用错误:代码里误写 `sc_icon_gift_flosc_bg` / `sc_icon_luck_gift_flosc_*`,实际资源文件名为 `float`,导致点击送礼后飘屏背景图加载失败,相关动画无法正常显示。
|
||||||
|
- 已继续定位语言房礼物特效不播放的根因:后端礼物配置返回的特效标记为 `ANIMATION`,而前端历史代码只识别拼写错误的 `ANIMSCION`,导致送礼后直接跳过全屏/半屏特效播放;现已改为同时兼容两种标记。
|
||||||
|
- 已继续优化语言房礼物特效播放性能:将礼物资源预热改为“后台串行预加载 + 选中礼物高优先级预加载 + 播放阶段复用同一解码/下载任务”,避免送礼时重复下载或重复解码同一个 `.svga/.mp4`;同时为全屏播放器增加 `RepaintBoundary`,并改为按原始比例渲染、关闭越界绘制,减少整页重绘和过度放大带来的卡顿。
|
||||||
- 已修复首页房间封面显示链路:兼容接口 `roomCover/cover` 双字段,并补齐编辑房间成功后的封面内存回写。
|
- 已修复首页房间封面显示链路:兼容接口 `roomCover/cover` 双字段,并补齐编辑房间成功后的封面内存回写。
|
||||||
|
- 已继续修复房间设置保存封面后丢失的问题:房间保存接口改为兼容提交 `id/roomId` 与 `roomCover/cover`,并在保存成功后优先保留刚提交的封面、名称、公告,避免被空响应覆盖。
|
||||||
|
- 已新增按房间 ID 的本地房间资料缓存兜底:首页、我的房间、历史/关注、搜索和再次进入房间都会优先回填最近一次本地保存的封面;同时已将房间封面空态图替换为新的 `sc_no_data.png`。
|
||||||
|
- 已确认房间封面上传接口与 `PUT /room/profile` 保存接口都返回了正确图片地址,并修正首页/重进房间的取值优先级,避免被列表接口短时间返回的旧封面或无效封面重新覆盖。
|
||||||
|
- 已继续完善个人主页头像与滚动表现:顶部小头像保持纯头像,主头像恢复头像框叠加;同时将个人主页背景并入可滚动 header,修复滑动时内容与背景不同步的问题。
|
||||||
|
- 已进一步统一头像框方案:共享头像组件改为“头像居中 + 头像框包边”结构,并对这类静态头像框资源增加白底去除处理,不再简单把头像框整张盖在头像上;个人主页主头像与房间麦位头像现已同步采用这一展示方式。
|
||||||
|
- 已修复静态头像框透明区域误变黑的问题:调整头像框白底透明化公式后,进入房间时不再出现只有头像附近有颜色、其余房间背景被黑块覆盖的现象。
|
||||||
|
- 已修复个人主页首屏黑屏与内容区多余圆角问题:为个人页补充稳定的底图/底色占位,避免进入页面时先出现黑屏;同时移除资料内容区顶部多余圆角。
|
||||||
|
- 已将个人主页首屏占位升级为骨架屏:进入个人页时会先按真实版式展示头像、昵称、计数区、Tab 和资料内容的骨架块,不再先露默认背景等待约 1 秒。
|
||||||
|
- 已优化语言房顶部成员入口的点击范围:右上角在线成员区域现已将左侧头像堆叠区与右侧成员图标统一为同一点击热区,用户点击头像区也可直接打开成员列表页。
|
||||||
- 完成仓库结构、依赖引用、资源体积和 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 依赖以恢复构建。
|
||||||
@ -50,12 +63,32 @@
|
|||||||
|
|
||||||
## 已改动文件
|
## 已改动文件
|
||||||
- `需求进度.md`
|
- `需求进度.md`
|
||||||
|
- `lib/shared/data_sources/models/enum/sc_gift_type.dart`
|
||||||
|
- `lib/shared/tools/sc_gift_vap_svga_manager.dart`
|
||||||
|
- `lib/services/general/sc_app_general_manager.dart`
|
||||||
|
- `lib/ui_kit/widgets/room/floating/floating_gift_screen_widget.dart`
|
||||||
|
- `lib/ui_kit/widgets/room/floating/floating_luck_gift_screen_widget.dart`
|
||||||
- `lib/services/room/rc_room_manager.dart`
|
- `lib/services/room/rc_room_manager.dart`
|
||||||
|
- `lib/services/audio/rtc_manager.dart`
|
||||||
|
- `lib/modules/room/edit/room_edit_page.dart`
|
||||||
|
- `lib/shared/data_sources/sources/repositories/sc_user_repository_impl.dart`
|
||||||
|
- `lib/shared/data_sources/sources/repositories/sc_room_repository_imp.dart`
|
||||||
|
- `lib/shared/tools/sc_room_profile_cache.dart`
|
||||||
- `lib/shared/business_logic/models/res/room_res.dart`
|
- `lib/shared/business_logic/models/res/room_res.dart`
|
||||||
- `lib/shared/business_logic/models/res/follow_room_res.dart`
|
- `lib/shared/business_logic/models/res/follow_room_res.dart`
|
||||||
- `lib/shared/business_logic/models/res/my_room_res.dart`
|
- `lib/shared/business_logic/models/res/my_room_res.dart`
|
||||||
- `lib/shared/business_logic/models/res/sc_edit_room_info_res.dart`
|
- `lib/shared/business_logic/models/res/sc_edit_room_info_res.dart`
|
||||||
- `lib/shared/business_logic/models/res/join_room_res.dart`
|
- `lib/shared/business_logic/models/res/join_room_res.dart`
|
||||||
|
- `lib/ui_kit/components/sc_compontent.dart`
|
||||||
|
- `lib/ui_kit/components/sc_float_ichart.dart`
|
||||||
|
- `lib/ui_kit/widgets/room/room_head_widget.dart`
|
||||||
|
- `lib/modules/room/detail/room_detail_page.dart`
|
||||||
|
- `lib/modules/home/popular/party/sc_home_party_page.dart`
|
||||||
|
- `lib/modules/home/popular/follow/sc_room_follow_page.dart`
|
||||||
|
- `lib/modules/home/popular/history/sc_room_history_page.dart`
|
||||||
|
- `lib/modules/home/popular/mine/sc_home_mine_page.dart`
|
||||||
|
- `lib/modules/search/sc_search_page.dart`
|
||||||
|
- `sc_images/general/sc_no_data.png`
|
||||||
- `.gitignore`
|
- `.gitignore`
|
||||||
- `android/key.properties`
|
- `android/key.properties`
|
||||||
- `pubspec.yaml`
|
- `pubspec.yaml`
|
||||||
@ -81,8 +114,21 @@
|
|||||||
- `assets/`
|
- `assets/`
|
||||||
|
|
||||||
## 已验证结果
|
## 已验证结果
|
||||||
|
- 已通过代码与资源目录交叉核对确认:房内礼物飘屏当前报错 `Unable to load asset: "sc_images/room/sc_icon_gift_flosc_bg.png"` 的根因是资源名拼写错误,而不是消息未下发;工程内实际存在的资源文件为 `sc_icon_gift_float_bg.png`、`sc_icon_luck_gift_float_bg1~5.png`、`sc_icon_luck_gift_float_n_bg.png`。
|
||||||
|
- 已确认当前“点击赠送没有动画”至少包含一类前端资源问题:大额礼物/幸运礼物飘屏 widget 已进入渲染,但因为背景图 asset 路径写错而无法显示;全屏 `SVGA/VAP` 特效是否播放仍取决于具体礼物是否带有 `giftSourceUrl`,且 `special` 包含 `ANIMSCION/ANIMATION/GLOBAL_GIFT` 之一。
|
||||||
|
- 已完成一轮针对卡顿的代码级优化:礼物列表现在只会对真正需要全屏特效的礼物做受控预热;用户选中礼物时会立即高优先级预热该礼物;播放器播放时优先复用内存里的 `MovieEntity` 或同一路径的在途任务,不再重复发起网络下载/重复解码;渲染层也已改为独立重绘边界以减少房间整页跟随重绘。
|
||||||
|
- 已通过 GiftFX 调试日志确认:示例礼物 `阿拉伯舞蹈` 实际返回 `giftSourceUrl=.svga` 且 `special=ANIMATION`,此前前端因为不识别该标记而输出 `skip local play because special does not include ANIMSCION/GLOBAL_GIFT`;本轮已补齐 `ANIMATION` 兼容判断。
|
||||||
- 当前工作区存在用户已有未提交改动,后续处理均避开覆盖。
|
- 当前工作区存在用户已有未提交改动,后续处理均避开覆盖。
|
||||||
- 首页 `party/follow/history/mine` 以及房间详情使用的房间封面模型已兼容 `roomCover` 与 `cover` 两种返回字段,上传封面后不会再因为字段名不一致掉回空占位。
|
- 首页 `party/follow/history/mine` 以及房间详情使用的房间封面模型已兼容 `roomCover` 与 `cover` 两种返回字段,上传封面后不会再因为字段名不一致掉回空占位。
|
||||||
|
- 房间设置保存封面时,前端现在会同时兼容提交和读取两套字段,并在 `PUT /room/profile` 响应或紧接着的房间详情刷新返回空封面时保留刚上传的封面,不再被空值冲掉。
|
||||||
|
- 已新增本地房间封面兜底缓存:即使服务端短时间仍返回空封面,首页列表、我的房间、搜索结果、房间头部、房间详情和再次进入房间也会继续显示最近一次本地成功保存的封面。
|
||||||
|
- 已通过实际请求日志确认:`POST /external/oss/upload`、`PUT /room/profile` 和随后 `GET /room/profile/specific` 都已返回正确封面 URL;本轮问题的根因已收敛为首页/列表链路仍可能使用接口短时间回传的旧值,因此本地成功保存的封面现已提升为更高优先级。
|
||||||
|
- 房间封面空态图已切换为新的 `sc_images/general/sc_no_data.png`,其资源尺寸为 `288x288`。
|
||||||
|
- 房间资料卡与个人主页使用的用户资料链路已重新对齐:个人页现兼容 `avatar` 别名;顶部小头像不叠加头像框,但主头像会继续叠加头像框资源,且个人页背景已随 header 一起滚动,不再出现内容先滑动、背景停留不动的现象。
|
||||||
|
- 头像框显示逻辑已改为“框在外、头像在内”的常见样式;针对当前这类带白底的静态头像框资源,已增加白底透明化处理,因此个人主页主头像与房间麦位头像都不会再留下明显白边。
|
||||||
|
- 静态头像框的透明区域现已保持透明,不会再被错误算成黑色;房间麦位头像、个人主页主头像和其它复用 `head()` 的头像组件进入房间后不会再出现大块黑底遮罩。
|
||||||
|
- 个人主页现在在数据加载前也会先展示默认背景和底色,不会再闪黑;“About me / Giftwall” 内容区顶部边缘已改为直角,不再额外出现一个圆角缺口。
|
||||||
|
- 个人主页当前已接入贴合页面结构的骨架屏,占位期间不会先看到背景图单独显示;资料接口返回后再无缝切到真实内容,首屏观感更平滑。
|
||||||
- 目录体积排查显示:`build/` 约 `4.7G`、`.dart_tool/` 约 `347M`、`ios/` 约 `250M`、`sc_images/` 约 `48M`、`local_packages/` 约 `6.1M`。
|
- 目录体积排查显示:`build/` 约 `4.7G`、`.dart_tool/` 约 `347M`、`ios/` 约 `250M`、`sc_images/` 约 `48M`、`local_packages/` 约 `6.1M`。
|
||||||
- 当前包体过大的主要原因已经确认:
|
- 当前包体过大的主要原因已经确认:
|
||||||
- universal APK 带入了 `arm64-v8a`、`armeabi-v7a`、`x86_64` 三套 ABI。
|
- universal APK 带入了 `arm64-v8a`、`armeabi-v7a`、`x86_64` 三套 ABI。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user