1248 lines
44 KiB
Dart
1248 lines
44 KiB
Dart
import 'dart:convert';
|
||
import 'dart:ui' as ui;
|
||
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_debouncer/flutter_debouncer.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||
import 'package:yumi/app_localizations.dart';
|
||
import 'package:yumi/ui_kit/components/sc_compontent.dart';
|
||
import 'package:yumi/ui_kit/components/text/sc_text.dart';
|
||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||
import 'package:yumi/ui_kit/theme/socialchat_theme.dart';
|
||
import 'package:yumi/app/constants/sc_global_config.dart';
|
||
import 'package:yumi/app/config/business_logic_strategy.dart';
|
||
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
||
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
|
||
import 'package:yumi/shared/data_sources/sources/repositories/sc_room_repository_imp.dart';
|
||
import 'package:yumi/shared/business_logic/models/res/login_res.dart';
|
||
import 'package:yumi/main.dart';
|
||
import 'package:provider/provider.dart';
|
||
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
||
import 'package:yumi/app/constants/sc_screen.dart';
|
||
import 'package:yumi/app/routes/sc_fluro_navigator.dart';
|
||
import 'package:yumi/shared/tools/sc_dialog_utils.dart';
|
||
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.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/services/general/sc_app_general_manager.dart';
|
||
import 'package:yumi/services/audio/rtc_manager.dart';
|
||
import 'package:yumi/services/audio/rtm_manager.dart';
|
||
import 'package:yumi/services/auth/user_profile_manager.dart';
|
||
import 'package:yumi/ui_kit/widgets/countdown_timer.dart';
|
||
import 'package:yumi/ui_kit/widgets/room/room_msg_item.dart';
|
||
import 'package:yumi/modules/wallet/wallet_route.dart';
|
||
import 'package:yumi/modules/gift/gift_tab_page.dart';
|
||
import '../../shared/data_sources/models/enum/sc_gift_type.dart';
|
||
import '../../shared/data_sources/models/message/sc_floating_message.dart';
|
||
import '../../shared/business_logic/usecases/sc_fixed_width_tabIndicator.dart';
|
||
|
||
class _GiftPageTabItem {
|
||
const _GiftPageTabItem({required this.type, required this.label});
|
||
|
||
final String type;
|
||
final String label;
|
||
}
|
||
|
||
class GiftPage extends StatefulWidget {
|
||
SocialChatUserProfile? toUser;
|
||
|
||
GiftPage({super.key, this.toUser});
|
||
|
||
@override
|
||
_GiftPageState createState() => _GiftPageState();
|
||
}
|
||
|
||
class _GiftPageState extends State<GiftPage>
|
||
with SingleTickerProviderStateMixin {
|
||
static const List<String> _preferredGiftTabOrder = <String>[
|
||
"ALL",
|
||
"ACTIVITY",
|
||
"LUCKY_GIFT",
|
||
"CP",
|
||
"MAGIC",
|
||
"CUSTOMIZED",
|
||
"NSCIONAL_FLAG",
|
||
];
|
||
|
||
TabController? _tabController;
|
||
List<String> _tabTypes = <String>[];
|
||
|
||
/// 业务逻辑策略访问器
|
||
BusinessLogicStrategy get _strategy => SCGlobalConfig.businessLogicStrategy;
|
||
|
||
// int checkedIndex = 0;
|
||
SocialChatGiftRes? checkedGift;
|
||
final Map<String, SocialChatGiftRes?> _selectedGiftByTab =
|
||
<String, SocialChatGiftRes?>{};
|
||
RtcProvider? rtcProvider;
|
||
|
||
bool isAll = false;
|
||
List<HeadSelect> listMai = [];
|
||
|
||
bool noShowNumber = false;
|
||
|
||
///选中人员
|
||
Set<String> set = {};
|
||
|
||
///数量的箭头是否朝上
|
||
bool isNumberUp = true;
|
||
|
||
///数量
|
||
int number = 1;
|
||
|
||
int giveType = 1;
|
||
|
||
int giftType = 0;
|
||
Debouncer debouncer = Debouncer();
|
||
|
||
void _giftFxLog(String message) {
|
||
debugPrint('[GiftFX][Send] $message');
|
||
}
|
||
|
||
void _applyGiftSelection(SocialChatGiftRes? gift, {bool notify = true}) {
|
||
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;
|
||
giftType = _giftTypeFromTab(gift?.giftTab);
|
||
if (notify) {
|
||
setState(() {});
|
||
}
|
||
}
|
||
|
||
void _handleGiftSelected(String tabType, SocialChatGiftRes? gift) {
|
||
_selectedGiftByTab[tabType] = gift;
|
||
_applyGiftSelection(gift);
|
||
}
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
rtcProvider = Provider.of<RtcProvider>(context, listen: false);
|
||
Provider.of<SCAppGeneralManager>(context, listen: false).giftList();
|
||
Provider.of<SCAppGeneralManager>(context, listen: false).giftActivityList();
|
||
// Provider.of<GeneralProvider>(context, listen: false).giftBackpack();
|
||
Provider.of<SocialChatUserProfileManager>(context, listen: false).balance();
|
||
rtcProvider?.roomWheatMap.forEach((k, v) {
|
||
if (v.user != null) {
|
||
if (v.user?.id == AccountStorage().getCurrentUser()?.userProfile?.id) {
|
||
listMai.add(HeadSelect(true, v));
|
||
} else {
|
||
listMai.add(HeadSelect(false, v));
|
||
}
|
||
}
|
||
isAll = true;
|
||
for (var mai in listMai) {
|
||
if (!mai.isSelect) {
|
||
isAll = false;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_tabController?.removeListener(_handleTabChanged);
|
||
_tabController?.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
void _handleTabChanged() {
|
||
final controller = _tabController;
|
||
if (!mounted ||
|
||
controller == null ||
|
||
controller.index >= _tabTypes.length) {
|
||
return;
|
||
}
|
||
final ref = Provider.of<SCAppGeneralManager>(context, listen: false);
|
||
_syncSelectedGiftForTab(ref, _tabTypes[controller.index]);
|
||
}
|
||
|
||
List<_GiftPageTabItem> _buildGiftTabs(
|
||
BuildContext context,
|
||
SCAppGeneralManager ref,
|
||
) {
|
||
final localizations = SCAppLocalizations.of(context)!;
|
||
final availableTypes =
|
||
ref.giftByTab.entries
|
||
.where((entry) => entry.value.isNotEmpty)
|
||
.map((entry) => entry.key)
|
||
.toList();
|
||
final orderedTypes = <String>[];
|
||
for (final type in _preferredGiftTabOrder) {
|
||
if (availableTypes.remove(type)) {
|
||
orderedTypes.add(type);
|
||
}
|
||
}
|
||
orderedTypes.addAll(availableTypes);
|
||
if (orderedTypes.isEmpty) {
|
||
orderedTypes.add("ALL");
|
||
}
|
||
return orderedTypes
|
||
.map(
|
||
(type) => _GiftPageTabItem(
|
||
type: type,
|
||
label: _giftTabLabel(localizations, type),
|
||
),
|
||
)
|
||
.toList();
|
||
}
|
||
|
||
void _ensureTabController(
|
||
SCAppGeneralManager ref,
|
||
List<_GiftPageTabItem> tabs,
|
||
) {
|
||
final nextTypes = tabs.map((tab) => tab.type).toList();
|
||
if (_tabController != null && listEquals(_tabTypes, nextTypes)) {
|
||
return;
|
||
}
|
||
|
||
final currentType =
|
||
(_tabController != null &&
|
||
_tabTypes.isNotEmpty &&
|
||
_tabController!.index < _tabTypes.length)
|
||
? _tabTypes[_tabController!.index]
|
||
: null;
|
||
final nextIndex = currentType != null ? nextTypes.indexOf(currentType) : 0;
|
||
_tabController?.removeListener(_handleTabChanged);
|
||
_tabController?.dispose();
|
||
_tabTypes = nextTypes;
|
||
_tabController = TabController(
|
||
length: tabs.length,
|
||
vsync: this,
|
||
initialIndex: nextIndex >= 0 ? nextIndex : 0,
|
||
)..addListener(_handleTabChanged);
|
||
_syncSelectedGiftForTab(
|
||
ref,
|
||
_tabTypes[_tabController!.index],
|
||
notify: false,
|
||
);
|
||
}
|
||
|
||
void _syncSelectedGiftForTab(
|
||
SCAppGeneralManager ref,
|
||
String tabType, {
|
||
bool notify = true,
|
||
}) {
|
||
final gifts = ref.giftByTab[tabType] ?? const <SocialChatGiftRes>[];
|
||
final gift = _resolveSelectedGift(tabType, gifts);
|
||
_selectedGiftByTab[tabType] = gift;
|
||
_applyGiftSelection(gift, notify: notify);
|
||
}
|
||
|
||
void _ensureCurrentTabSelection(SCAppGeneralManager ref, String tabType) {
|
||
final gifts = ref.giftByTab[tabType] ?? const <SocialChatGiftRes>[];
|
||
if (_matchesCurrentGift(gifts)) {
|
||
return;
|
||
}
|
||
final gift = _resolveSelectedGift(tabType, gifts);
|
||
_selectedGiftByTab[tabType] = gift;
|
||
_applyGiftSelection(gift, notify: false);
|
||
}
|
||
|
||
SocialChatGiftRes? _resolveSelectedGift(
|
||
String tabType,
|
||
List<SocialChatGiftRes> gifts,
|
||
) {
|
||
if (gifts.isEmpty) {
|
||
return null;
|
||
}
|
||
|
||
final selectedGift = _selectedGiftByTab[tabType];
|
||
if (selectedGift?.id != null) {
|
||
for (final gift in gifts) {
|
||
if (gift.id == selectedGift!.id) {
|
||
return gift;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (tabType == "CUSTOMIZED") {
|
||
return gifts.length > 1 ? gifts[1] : null;
|
||
}
|
||
return gifts.first;
|
||
}
|
||
|
||
bool _matchesCurrentGift(List<SocialChatGiftRes> gifts) {
|
||
final currentGiftId = checkedGift?.id;
|
||
if (currentGiftId == null) {
|
||
return gifts.isEmpty;
|
||
}
|
||
return gifts.any((gift) => gift.id == currentGiftId);
|
||
}
|
||
|
||
String _giftTabLabel(SCAppLocalizations localizations, String tabType) {
|
||
switch (tabType) {
|
||
case "ALL":
|
||
return localizations.all;
|
||
case "ACTIVITY":
|
||
return localizations.activity;
|
||
case "LUCK":
|
||
case "LUCKY_GIFT":
|
||
return localizations.luck;
|
||
case "CP":
|
||
return "CP";
|
||
case "MAGIC":
|
||
return localizations.magic;
|
||
case "CUSTOMIZED":
|
||
return localizations.customized;
|
||
case "NSCIONAL_FLAG":
|
||
return localizations.country;
|
||
default:
|
||
return tabType
|
||
.toLowerCase()
|
||
.split("_")
|
||
.map(
|
||
(word) =>
|
||
word.isEmpty
|
||
? word
|
||
: "${word[0].toUpperCase()}${word.substring(1)}",
|
||
)
|
||
.join(" ");
|
||
}
|
||
}
|
||
|
||
int _giftTypeFromTab(String? tabType) {
|
||
switch (tabType) {
|
||
case "ACTIVITY":
|
||
return 1;
|
||
case "LUCK":
|
||
case "LUCKY_GIFT":
|
||
return 2;
|
||
case "CP":
|
||
return 3;
|
||
case "MAGIC":
|
||
return 5;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Consumer<SCAppGeneralManager>(
|
||
builder: (context, ref, child) {
|
||
final giftTabs = _buildGiftTabs(context, ref);
|
||
_ensureTabController(ref, giftTabs);
|
||
final tabController = _tabController;
|
||
if (tabController == null) {
|
||
return const SizedBox.shrink();
|
||
}
|
||
_ensureCurrentTabSelection(ref, giftTabs[tabController.index].type);
|
||
return SafeArea(
|
||
top: false,
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.end,
|
||
children: [
|
||
SCGlobalConfig.isReview
|
||
? Container()
|
||
: ((AccountStorage()
|
||
.getCurrentUser()
|
||
?.userProfile
|
||
?.firstRecharge ??
|
||
false)
|
||
? GestureDetector(
|
||
child: Stack(
|
||
alignment: AlignmentDirectional.bottomEnd,
|
||
children: [
|
||
Transform.flip(
|
||
flipX:
|
||
SCGlobalConfig.lang == "ar"
|
||
? true
|
||
: false, // 水平翻转
|
||
flipY: false, // 垂直翻转设为 false
|
||
child: Image.asset(
|
||
_strategy
|
||
.getGiftPageFirstRechargeRoomTagIcon(),
|
||
height: 75.w,
|
||
),
|
||
),
|
||
SCGlobalConfig.lang == "ar"
|
||
? PositionedDirectional(
|
||
end: 22.w,
|
||
bottom: 38.w,
|
||
child: Image.asset(
|
||
_strategy
|
||
.getGiftPageFirstRechargeTextIcon(
|
||
'ar',
|
||
),
|
||
height: 13.w,
|
||
),
|
||
)
|
||
: PositionedDirectional(
|
||
end: 16.w,
|
||
bottom: 38.w,
|
||
child: Image.asset(
|
||
_strategy
|
||
.getGiftPageFirstRechargeTextIcon(
|
||
'en',
|
||
),
|
||
height: 13.w,
|
||
),
|
||
),
|
||
PositionedDirectional(
|
||
end: 34.w,
|
||
bottom: 13.w,
|
||
child: CountdownTimer(
|
||
expiryDate:
|
||
DateTime.fromMillisecondsSinceEpoch(
|
||
AccountStorage()
|
||
.getCurrentUser()
|
||
?.userProfile
|
||
?.firstRechargeEndTime ??
|
||
0,
|
||
),
|
||
color: Colors.white,
|
||
fontSize: 12.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
onTap: () {
|
||
SCDialogUtils.showFirstRechargeDialog(
|
||
navigatorKey.currentState!.context,
|
||
);
|
||
},
|
||
)
|
||
: Container()),
|
||
],
|
||
),
|
||
_buildGiftHead(),
|
||
ClipRRect(
|
||
borderRadius: BorderRadius.only(
|
||
topLeft: Radius.circular(12.0),
|
||
topRight: Radius.circular(12.0),
|
||
),
|
||
child: BackdropFilter(
|
||
filter: ui.ImageFilter.blur(sigmaX: 15, sigmaY: 15),
|
||
child: Container(
|
||
color: const Color(0xff09372E).withValues(alpha: 0.5),
|
||
constraints: BoxConstraints(maxHeight: 430.w),
|
||
child: Column(
|
||
children: [
|
||
SizedBox(height: 12.w),
|
||
Row(
|
||
children: [
|
||
SizedBox(width: 8.w),
|
||
widget.toUser == null
|
||
? Builder(
|
||
builder: (ct) {
|
||
return GestureDetector(
|
||
child: Image.asset(
|
||
isAll
|
||
? "sc_images/room/sc_icon_gift_all_en.png"
|
||
: "sc_images/room/sc_icon_gift_all_no.png",
|
||
width: 28.w,
|
||
height: 28.w,
|
||
),
|
||
onTap: () {
|
||
isAll = !isAll;
|
||
for (var mai in listMai) {
|
||
mai.isSelect = isAll;
|
||
}
|
||
setState(() {});
|
||
// showGiveTypeDialog(ct);
|
||
},
|
||
);
|
||
},
|
||
)
|
||
: Container(),
|
||
SizedBox(width: 8.w),
|
||
widget.toUser == null
|
||
? Expanded(
|
||
child: SingleChildScrollView(
|
||
scrollDirection: Axis.horizontal,
|
||
child: Row(
|
||
children:
|
||
listMai
|
||
.map((e) => _maiHead(e))
|
||
.toList(),
|
||
),
|
||
),
|
||
)
|
||
: Stack(
|
||
alignment: AlignmentDirectional.center,
|
||
children: [
|
||
Container(
|
||
padding: EdgeInsets.all(2.w),
|
||
child: netImage(
|
||
url: widget.toUser?.userAvatar ?? "",
|
||
width: 26.w,
|
||
defaultImg:
|
||
_strategy
|
||
.getMePageDefaultAvatarImage(),
|
||
shape: BoxShape.circle,
|
||
),
|
||
),
|
||
PositionedDirectional(
|
||
bottom: 0,
|
||
end: 0,
|
||
child: Image.asset(
|
||
"sc_images/login/sc_icon_login_ser_select.png",
|
||
width: 10.w,
|
||
height: 10.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
SizedBox(width: 12.w),
|
||
],
|
||
),
|
||
Row(
|
||
children: [
|
||
SizedBox(width: 5.w),
|
||
Expanded(
|
||
child: SizedBox(
|
||
height: 28.w,
|
||
child: TabBar(
|
||
tabAlignment: TabAlignment.start,
|
||
labelPadding: EdgeInsets.symmetric(
|
||
horizontal: 8.w,
|
||
),
|
||
labelColor: SocialChatTheme.primaryLight,
|
||
|
||
indicatorWeight: 0,
|
||
isScrollable: true,
|
||
indicator: SCFixedWidthTabIndicator(
|
||
width: 15.w,
|
||
color: SocialChatTheme.primaryLight,
|
||
),
|
||
unselectedLabelColor: Colors.white54,
|
||
labelStyle: TextStyle(fontSize: 14.sp),
|
||
unselectedLabelStyle: TextStyle(
|
||
fontSize: 12.sp,
|
||
),
|
||
indicatorColor: Colors.transparent,
|
||
dividerColor: Colors.transparent,
|
||
controller: tabController,
|
||
tabs:
|
||
giftTabs
|
||
.map((tab) => Tab(text: tab.label))
|
||
.toList(),
|
||
),
|
||
),
|
||
),
|
||
SizedBox(width: 5.w),
|
||
],
|
||
),
|
||
Expanded(
|
||
child: TabBarView(
|
||
physics: NeverScrollableScrollPhysics(),
|
||
controller: tabController,
|
||
children:
|
||
giftTabs
|
||
.map(
|
||
(tab) => GiftTabPage(
|
||
key: ValueKey(tab.type),
|
||
tab.type,
|
||
(gift) =>
|
||
_handleGiftSelected(tab.type, gift),
|
||
),
|
||
)
|
||
.toList(),
|
||
),
|
||
),
|
||
Row(
|
||
children: [
|
||
SizedBox(width: 15.w),
|
||
GestureDetector(
|
||
onTap: () {
|
||
SmartDialog.dismiss(tag: "showGiftControl");
|
||
SCNavigatorUtils.push(
|
||
navigatorKey.currentState!.context,
|
||
WalletRoute.recharge,
|
||
replace: false,
|
||
);
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.symmetric(
|
||
vertical: 8.w,
|
||
horizontal: 8.w,
|
||
),
|
||
width: 120.w,
|
||
decoration: BoxDecoration(
|
||
color: Colors.white10,
|
||
borderRadius: BorderRadius.circular(5),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Image.asset(
|
||
_strategy.getGiftPageGoldCoinIcon(),
|
||
width: 14.w,
|
||
height: 14.w,
|
||
),
|
||
SizedBox(width: 5.w),
|
||
Consumer<SocialChatUserProfileManager>(
|
||
builder: (context, ref, child) {
|
||
return Expanded(
|
||
child: text(
|
||
"${ref.myBalance}",
|
||
fontSize: 12.sp,
|
||
),
|
||
);
|
||
},
|
||
),
|
||
SizedBox(width: 5.w),
|
||
Icon(
|
||
Icons.arrow_forward_ios,
|
||
color: Colors.white,
|
||
size: 14.w,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
Spacer(),
|
||
Builder(
|
||
builder: (ct) {
|
||
return GestureDetector(
|
||
behavior: HitTestBehavior.opaque,
|
||
onTap: () {
|
||
if (noShowNumber) {
|
||
return;
|
||
}
|
||
isNumberUp = false;
|
||
_showNumber(ct);
|
||
setState(() {});
|
||
},
|
||
child: Container(
|
||
decoration: BoxDecoration(
|
||
color: Colors.white10,
|
||
borderRadius: BorderRadius.circular(5),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
SizedBox(width: 10.w),
|
||
text("$number", fontSize: 12.sp),
|
||
Icon(
|
||
isNumberUp
|
||
? Icons.keyboard_arrow_up
|
||
: Icons.keyboard_arrow_down,
|
||
color: Colors.white,
|
||
size: 20.w,
|
||
),
|
||
SizedBox(width: 5.w),
|
||
GestureDetector(
|
||
onTap: () {
|
||
giveGifts();
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.symmetric(
|
||
vertical: 8.w,
|
||
horizontal: 20.w,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color:
|
||
SocialChatTheme.primaryLight,
|
||
borderRadius:
|
||
BorderRadius.circular(5),
|
||
),
|
||
child: text(
|
||
SCAppLocalizations.of(
|
||
context,
|
||
)!.send,
|
||
fontSize: 14.sp,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
},
|
||
),
|
||
SizedBox(width: 15.w),
|
||
],
|
||
),
|
||
SizedBox(height: 15.w),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
void showGiveTypeDialog(BuildContext ct) {
|
||
SmartDialog.showAttach(
|
||
tag: "showGiveType",
|
||
targetContext: ct,
|
||
alignment: Alignment.bottomCenter,
|
||
animationType: SmartAnimationType.fade,
|
||
scalePointBuilder: (selfSize) => Offset(selfSize.width, 10),
|
||
builder: (_) {
|
||
return Container(
|
||
height: 135.w,
|
||
width: 200.w,
|
||
decoration: BoxDecoration(
|
||
image: DecorationImage(
|
||
image: AssetImage(_strategy.getGiftPageGiveTypeBackground()),
|
||
fit: BoxFit.fill,
|
||
),
|
||
),
|
||
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w),
|
||
child: Column(
|
||
children: [
|
||
SizedBox(height: 5.w),
|
||
Expanded(
|
||
child: GestureDetector(
|
||
child: Row(
|
||
children: [
|
||
Image.asset(
|
||
_strategy.getGiftPageAllOnMicrophoneIcon(),
|
||
height: 18.w,
|
||
width: 18.w,
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Expanded(
|
||
child: text(
|
||
SCAppLocalizations.of(context)!.allOnMicrophone,
|
||
fontSize: 14.sp,
|
||
textColor: Colors.white54,
|
||
),
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Image.asset(
|
||
giveType == 0
|
||
? _strategy.getCommonSelectIcon()
|
||
: _strategy.getCommonUnselectIcon(),
|
||
width: 15.w,
|
||
height: 15.w,
|
||
),
|
||
],
|
||
),
|
||
onTap: () {
|
||
giveType = 0;
|
||
SmartDialog.dismiss(tag: "showGiveType");
|
||
selecteAllMicUsers();
|
||
setState(() {});
|
||
},
|
||
),
|
||
),
|
||
Expanded(
|
||
child: GestureDetector(
|
||
child: Row(
|
||
children: [
|
||
Image.asset(
|
||
_strategy.getGiftPageUsersOnMicrophoneIcon(),
|
||
height: 18.w,
|
||
width: 18.w,
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Expanded(
|
||
child: text(
|
||
SCAppLocalizations.of(context)!.usersOnMicrophone,
|
||
fontSize: 14.sp,
|
||
textColor: Colors.white54,
|
||
),
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Image.asset(
|
||
giveType == 1
|
||
? _strategy.getCommonSelectIcon()
|
||
: _strategy.getCommonUnselectIcon(),
|
||
width: 15.w,
|
||
height: 15.w,
|
||
),
|
||
],
|
||
),
|
||
onTap: () {
|
||
giveType = 1;
|
||
SmartDialog.dismiss(tag: "showGiveType");
|
||
setState(() {});
|
||
},
|
||
),
|
||
),
|
||
Expanded(
|
||
child: GestureDetector(
|
||
onTap: () {
|
||
giveType = 2;
|
||
SmartDialog.dismiss(tag: "showGiveType");
|
||
setState(() {});
|
||
},
|
||
behavior: HitTestBehavior.opaque,
|
||
child: Row(
|
||
children: [
|
||
Image.asset(
|
||
_strategy.getGiftPageAllInTheRoomIcon(),
|
||
height: 18.w,
|
||
width: 18.w,
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Expanded(
|
||
child: text(
|
||
SCAppLocalizations.of(context)!.allInTheRoom,
|
||
fontSize: 14.sp,
|
||
textColor: Colors.white54,
|
||
),
|
||
),
|
||
SizedBox(width: 8.w),
|
||
Image.asset(
|
||
giveType == 2
|
||
? _strategy.getCommonSelectIcon()
|
||
: _strategy.getCommonUnselectIcon(),
|
||
width: 15.w,
|
||
height: 15.w,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
Widget _maiHead(HeadSelect shead) {
|
||
// 是否选中
|
||
return GestureDetector(
|
||
onTap: () {
|
||
isAll = true;
|
||
for (var mai in listMai) {
|
||
if (shead.mic?.user?.id == mai.mic?.user?.id) {
|
||
mai.isSelect = !mai.isSelect;
|
||
}
|
||
if (!mai.isSelect) {
|
||
isAll = false;
|
||
}
|
||
}
|
||
|
||
setState(() {});
|
||
},
|
||
child: Stack(
|
||
alignment: Alignment.bottomCenter,
|
||
clipBehavior: Clip.none,
|
||
children: <Widget>[
|
||
Stack(
|
||
alignment: AlignmentDirectional.center,
|
||
children: [
|
||
Container(
|
||
padding: EdgeInsets.all(2.w),
|
||
child: netImage(
|
||
url: shead.mic?.user?.userAvatar ?? "",
|
||
width: 26.w,
|
||
defaultImg: _strategy.getMePageDefaultAvatarImage(),
|
||
shape: BoxShape.circle,
|
||
),
|
||
),
|
||
PositionedDirectional(
|
||
bottom: 0,
|
||
end: 0,
|
||
child: Container(
|
||
alignment: AlignmentDirectional.center,
|
||
width: 12.w,
|
||
height: 12.w,
|
||
decoration: BoxDecoration(
|
||
color: Colors.black38,
|
||
shape: BoxShape.circle,
|
||
border:
|
||
shead.isSelect
|
||
? Border.all(
|
||
color: SocialChatTheme.primaryLight,
|
||
width: 1.w,
|
||
)
|
||
: null,
|
||
),
|
||
child: text(
|
||
"${shead.mic?.micIndex}",
|
||
textColor: Colors.white,
|
||
fontSize: 8.sp,
|
||
),
|
||
),
|
||
),
|
||
shead.isSelect
|
||
? PositionedDirectional(
|
||
bottom: 0,
|
||
end: 0,
|
||
child: Image.asset(
|
||
"sc_images/login/sc_icon_login_ser_select.png",
|
||
width: 12.w,
|
||
height: 12.w,
|
||
),
|
||
)
|
||
: Container(),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
///数量选项
|
||
void _showNumber(BuildContext ct) {
|
||
SmartDialog.showAttach(
|
||
tag: "showNumber",
|
||
targetContext: ct,
|
||
maskColor: Colors.transparent,
|
||
alignment: Alignment.topLeft,
|
||
animationType: SmartAnimationType.fade,
|
||
scalePointBuilder: (selfSize) => Offset(selfSize.width, 10),
|
||
onDismiss: () {
|
||
isNumberUp = true;
|
||
setState(() {});
|
||
},
|
||
builder: (_) {
|
||
return Transform.translate(
|
||
offset: Offset(SCGlobalConfig.lang == "ar" ? 20 : -20, -5),
|
||
child: CheckNumber(
|
||
onNumberChanged: (number) {
|
||
this.number = number;
|
||
isNumberUp = true;
|
||
setState(() {});
|
||
},
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
///选中所有在座位上的用户
|
||
void selecteAllMicUsers() {
|
||
for (var mai in listMai) {
|
||
mai.isSelect = true;
|
||
}
|
||
}
|
||
|
||
///赠送礼物
|
||
void giveGifts() {
|
||
List<String> acceptUserIds = [];
|
||
List<MicRes> acceptUsers = [];
|
||
|
||
if (widget.toUser != null) {
|
||
acceptUserIds.add(widget.toUser?.id ?? "");
|
||
acceptUsers.add(MicRes(user: widget.toUser));
|
||
} else {
|
||
///所有在线
|
||
if (giveType == 2) {
|
||
for (var value
|
||
in Provider.of<RtcProvider>(context, listen: false).onlineUsers) {
|
||
acceptUsers.add(MicRes(user: value));
|
||
acceptUserIds.add(value.id ?? "");
|
||
}
|
||
} else {
|
||
for (var mu in listMai) {
|
||
if (mu.isSelect) {
|
||
acceptUsers.add(mu.mic!);
|
||
acceptUserIds.add(mu.mic!.user!.id ?? "");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (acceptUserIds.isEmpty) {
|
||
SCTts.show(SCAppLocalizations.of(context)!.pleaseSelectTheRecipient);
|
||
return;
|
||
}
|
||
if (checkedGift == null) {
|
||
return;
|
||
}
|
||
SCChatRoomRepository()
|
||
.giveGift(
|
||
acceptUserIds,
|
||
checkedGift!.id ?? "",
|
||
number,
|
||
false,
|
||
roomId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||
)
|
||
.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);
|
||
sendGiftMsg(acceptUsers);
|
||
Provider.of<SocialChatUserProfileManager>(
|
||
context,
|
||
listen: false,
|
||
).updateBalance(result);
|
||
})
|
||
.catchError((e) {
|
||
_giftFxLog(
|
||
'giveGift failed giftId=${checkedGift?.id} '
|
||
'giftName=${checkedGift?.giftName} '
|
||
'error=$e',
|
||
);
|
||
});
|
||
}
|
||
|
||
void sendGiftMsg(List<MicRes> acceptUsers) {
|
||
///发送一条IM消息
|
||
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>(
|
||
navigatorKey.currentState!.context,
|
||
listen: false,
|
||
).dispatchMessage(
|
||
Msg(
|
||
groupId:
|
||
rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount,
|
||
gift: checkedGift,
|
||
user: AccountStorage().getCurrentUser()?.userProfile,
|
||
toUser: u.user,
|
||
number: number,
|
||
type: SCRoomMsgType.gift,
|
||
role:
|
||
Provider.of<RtcProvider>(
|
||
navigatorKey.currentState!.context,
|
||
listen: false,
|
||
).currenRoom?.entrants?.roles ??
|
||
"",
|
||
msg: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||
),
|
||
addLocal: true,
|
||
);
|
||
if (rtcProvider?.currenRoom?.roomProfile?.roomSetting?.showHeartbeat ??
|
||
false) {
|
||
debouncer.debounce(
|
||
duration: Duration(milliseconds: 350),
|
||
onDebounce: () {
|
||
Provider.of<RtcProvider>(
|
||
navigatorKey.currentState!.context,
|
||
listen: false,
|
||
).retrieveMicrophoneList();
|
||
},
|
||
);
|
||
}
|
||
num coins = checkedGift!.giftCandy! * number;
|
||
if (coins > 9999) {
|
||
var fMsg = SCFloatingMessage(
|
||
type: 1,
|
||
userAvatarUrl:
|
||
AccountStorage().getCurrentUser()?.userProfile?.userAvatar ?? "",
|
||
userName:
|
||
AccountStorage().getCurrentUser()?.userProfile?.userNickname ??
|
||
"",
|
||
toUserName: u.user?.userNickname ?? "",
|
||
giftUrl: checkedGift?.giftPhoto ?? "",
|
||
roomId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.id ?? "",
|
||
coins: coins,
|
||
number: number,
|
||
);
|
||
OverlayManager().addMessage(fMsg);
|
||
}
|
||
if (checkedGift!.giftSourceUrl != null && checkedGift!.special != null) {
|
||
if (scGiftHasFullScreenEffect(checkedGift!.special)) {
|
||
if (SCGlobalConfig.isGiftSpecialEffects) {
|
||
_giftFxLog(
|
||
'local trigger player play '
|
||
'path=${checkedGift!.giftSourceUrl} '
|
||
'giftId=${checkedGift?.id} '
|
||
'giftName=${checkedGift?.giftName}',
|
||
);
|
||
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}',
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 将数字giftType转换为字符串类型,用于活动礼物头部背景
|
||
String _giftTypeToString(int giftType) {
|
||
switch (giftType) {
|
||
case 1: // ACTIVITY
|
||
return 'ACTIVITY';
|
||
case 2: // LUCKY_GIFT -> LUCK
|
||
return 'LUCK';
|
||
case 3: // CP
|
||
return 'CP';
|
||
case 5: // MAGIC
|
||
return 'MAGIC';
|
||
default:
|
||
return 'ACTIVITY';
|
||
}
|
||
}
|
||
|
||
_buildGiftHead() {
|
||
if (giftType == 1 || giftType == 2 || giftType == 3 || giftType == 5) {
|
||
// 获取基础路径
|
||
String basePath = _strategy.getGiftPageActivityGiftHeadBackground(
|
||
_giftTypeToString(giftType),
|
||
);
|
||
|
||
// 添加语言后缀
|
||
String imagePath;
|
||
if (SCGlobalConfig.lang == "ar") {
|
||
// 移除扩展名,添加 _ar 后缀,然后重新添加扩展名
|
||
if (basePath.endsWith('.png')) {
|
||
imagePath = basePath.substring(0, basePath.length - 4) + '_ar.png';
|
||
} else {
|
||
imagePath = basePath + '_ar';
|
||
}
|
||
} else {
|
||
if (basePath.endsWith('.png')) {
|
||
imagePath = basePath.substring(0, basePath.length - 4) + '_en.png';
|
||
} else {
|
||
imagePath = basePath + '_en';
|
||
}
|
||
}
|
||
|
||
// 确定高度
|
||
double height = giftType == 5 ? 80.w : 65.w;
|
||
|
||
return Container(
|
||
margin: EdgeInsets.symmetric(horizontal: 10.w),
|
||
child: Image.asset(imagePath, height: height, fit: BoxFit.fill),
|
||
);
|
||
}
|
||
return Container();
|
||
}
|
||
|
||
///发送一条消息,幸运礼物,房间里所有人都能看到礼物飘向麦位的动画
|
||
void sendLuckGiftAnimOtherMsg(List<MicRes> acceptUsers) {
|
||
Provider.of<RtmProvider>(
|
||
navigatorKey.currentState!.context,
|
||
listen: false,
|
||
).dispatchMessage(
|
||
Msg(
|
||
groupId: rtcProvider?.currenRoom?.roomProfile?.roomProfile?.roomAccount,
|
||
gift: checkedGift,
|
||
type: SCRoomMsgType.luckGiftAnimOther,
|
||
msg: jsonEncode(acceptUsers.map((u) => u.user?.id).toList()),
|
||
),
|
||
addLocal: false,
|
||
);
|
||
}
|
||
}
|
||
|
||
class CheckNumber extends StatelessWidget {
|
||
final Function(int) onNumberChanged;
|
||
late BuildContext context;
|
||
|
||
CheckNumber({super.key, required this.onNumberChanged});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
this.context = context;
|
||
return Container(
|
||
alignment: AlignmentDirectional.topEnd,
|
||
margin: EdgeInsets.only(right: width(22)),
|
||
child: ClipRRect(
|
||
borderRadius: BorderRadius.all(Radius.circular(12.w)),
|
||
child: BackdropFilter(
|
||
filter: ui.ImageFilter.blur(sigmaX: 25, sigmaY: 25),
|
||
child: Container(
|
||
decoration: BoxDecoration(
|
||
color: Colors.white10,
|
||
borderRadius: BorderRadius.all(Radius.circular(height(6))),
|
||
),
|
||
width: width(75),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: <Widget>[
|
||
SizedBox(height: height(10)),
|
||
_item(1),
|
||
_item(7),
|
||
_item(17),
|
||
_item(77),
|
||
_item(777),
|
||
_item(7777),
|
||
SizedBox(height: height(10)),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
_item(int number) {
|
||
return GestureDetector(
|
||
behavior: HitTestBehavior.opaque,
|
||
onTap: () {
|
||
SmartDialog.dismiss(tag: "showNumber");
|
||
onNumberChanged(number);
|
||
},
|
||
child: SizedBox(
|
||
height: height(24),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: <Widget>[
|
||
SizedBox(width: width(10)),
|
||
Text(
|
||
"$number",
|
||
style: TextStyle(
|
||
fontSize: sp(14),
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w400,
|
||
decoration: TextDecoration.none,
|
||
),
|
||
),
|
||
SizedBox(width: width(10)),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class HeadSelect {
|
||
bool isSelect = false;
|
||
MicRes? mic;
|
||
|
||
HeadSelect(this.isSelect, this.mic);
|
||
}
|