235 lines
8.0 KiB
Dart
235 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:yumi/app/constants/sc_room_msg_type.dart';
|
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
|
import 'package:yumi/services/audio/rtm_manager.dart';
|
|
import 'package:yumi/ui_kit/components/sc_debounce_widget.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/music/room_music_floating_entry.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/red_packet/room_red_packet_models.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/red_packet/room_red_packet_open_dialog.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/red_packet/room_red_packet_pending_cache.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/room_banner_view.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/room_game_bottom_sheet.dart';
|
|
|
|
class RoomPlayWidget extends StatefulWidget {
|
|
const RoomPlayWidget({super.key});
|
|
|
|
@override
|
|
State<RoomPlayWidget> createState() => _RoomPlayWidgetState();
|
|
}
|
|
|
|
class _RoomPlayWidgetState extends State<RoomPlayWidget> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SizedBox.expand(
|
|
child: Stack(
|
|
children: [
|
|
PositionedDirectional(
|
|
end: 15.w,
|
|
bottom: 150.w,
|
|
child: const RoomMusicFloatingEntry(),
|
|
),
|
|
PositionedDirectional(
|
|
end: 15.w,
|
|
bottom: 200.w,
|
|
child: const _RoomRedPacketFloatingEntry(),
|
|
),
|
|
PositionedDirectional(
|
|
end: 15.w,
|
|
bottom: 100.w,
|
|
child: const RoomGameEntryButton(),
|
|
),
|
|
PositionedDirectional(
|
|
end: 10.w,
|
|
bottom: 15.w,
|
|
child: SizedBox(width: 46.w, child: RoomBannerView()),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _RoomRedPacketFloatingEntry extends StatelessWidget {
|
|
const _RoomRedPacketFloatingEntry();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: Listenable.merge([
|
|
RoomRedPacketPendingCache.instance,
|
|
RoomRedPacketOpenDialog.claimedStateNotifier,
|
|
]),
|
|
builder: (context, child) {
|
|
return Consumer2<RtcProvider, RtmProvider>(
|
|
builder: (context, rtcProvider, rtmProvider, child) {
|
|
final snapshot = _RoomRedPacketEntrySnapshot.fromProviders(
|
|
rtcProvider: rtcProvider,
|
|
rtmProvider: rtmProvider,
|
|
);
|
|
if (snapshot.packets.isEmpty) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
return SCDebounceWidget(
|
|
onTap: () {
|
|
final freshSnapshot = _RoomRedPacketEntrySnapshot.fromProviders(
|
|
rtcProvider: Provider.of<RtcProvider>(context, listen: false),
|
|
rtmProvider: Provider.of<RtmProvider>(context, listen: false),
|
|
);
|
|
if (freshSnapshot.packets.isEmpty) {
|
|
return;
|
|
}
|
|
RoomRedPacketOpenDialog.showList(
|
|
context,
|
|
freshSnapshot.packets,
|
|
);
|
|
},
|
|
child: SizedBox(
|
|
width: 44.w,
|
|
height: 44.w,
|
|
child: Stack(
|
|
clipBehavior: Clip.none,
|
|
alignment: Alignment.center,
|
|
children: [
|
|
Container(
|
|
width: 42.w,
|
|
height: 42.w,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: Colors.black.withValues(alpha: 0.22),
|
|
border: Border.all(
|
|
color: const Color(0xFFFFD56A),
|
|
width: 1.3.w,
|
|
),
|
|
),
|
|
alignment: Alignment.center,
|
|
child: Image.asset(
|
|
'sc_images/room/red_packet/small_packet_icon.png',
|
|
width: 32.w,
|
|
height: 32.w,
|
|
fit: BoxFit.contain,
|
|
gaplessPlayback: true,
|
|
),
|
|
),
|
|
if (snapshot.packets.length > 1)
|
|
PositionedDirectional(
|
|
top: -2.w,
|
|
end: -2.w,
|
|
child: Container(
|
|
constraints: BoxConstraints(minWidth: 16.w),
|
|
height: 16.w,
|
|
padding: EdgeInsets.symmetric(horizontal: 4.w),
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFFF3D34),
|
|
borderRadius: BorderRadius.circular(8.w),
|
|
border: Border.all(color: Colors.white, width: 1.w),
|
|
),
|
|
child: Text(
|
|
snapshot.packets.length > 99
|
|
? '99+'
|
|
: snapshot.packets.length.toString(),
|
|
textScaler: TextScaler.noScaling,
|
|
maxLines: 1,
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 9.sp,
|
|
fontWeight: FontWeight.w800,
|
|
decoration: TextDecoration.none,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _RoomRedPacketEntrySnapshot {
|
|
const _RoomRedPacketEntrySnapshot(this.packets, this.signature);
|
|
|
|
factory _RoomRedPacketEntrySnapshot.fromProviders({
|
|
required RtcProvider rtcProvider,
|
|
required RtmProvider rtmProvider,
|
|
}) {
|
|
final packets = <RoomRedPacketUiData>[];
|
|
final currentRoomId =
|
|
rtcProvider.currenRoom?.roomProfile?.roomProfile?.id?.trim() ?? '';
|
|
for (final packet in rtcProvider.redPacketList.map(
|
|
RoomRedPacketUiData.fromListRes,
|
|
)) {
|
|
_addVisiblePacket(packets, packet);
|
|
}
|
|
for (final packet in RoomRedPacketPendingCache.instance.packetsForRoom(
|
|
currentRoomId,
|
|
)) {
|
|
_addVisiblePacket(packets, packet);
|
|
}
|
|
for (final msg in rtmProvider.roomChatMsgList) {
|
|
if (msg.type != SCRoomMsgType.roomRedPacket) {
|
|
continue;
|
|
}
|
|
_addVisiblePacket(
|
|
packets,
|
|
RoomRedPacketUiData.fromMessagePayload(
|
|
payload: msg.msg,
|
|
fallbackUser: msg.user,
|
|
),
|
|
);
|
|
}
|
|
final signature = packets
|
|
.map(
|
|
(item) =>
|
|
'${item.packetId}|${item.status}|${item.remainCount}|${item.grabbed}|${item.claimedAmount}|${item.claimStartTimeMs}|${item.expireTimeMs}',
|
|
)
|
|
.join(';');
|
|
return _RoomRedPacketEntrySnapshot(packets, signature);
|
|
}
|
|
|
|
final List<RoomRedPacketUiData> packets;
|
|
final String signature;
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) {
|
|
return true;
|
|
}
|
|
return other is _RoomRedPacketEntrySnapshot && other.signature == signature;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => signature.hashCode;
|
|
}
|
|
|
|
void _addVisiblePacket(
|
|
List<RoomRedPacketUiData> packets,
|
|
RoomRedPacketUiData packet,
|
|
) {
|
|
final normalizedPacket = RoomRedPacketOpenDialog.withLocalClaimState(packet);
|
|
if (!_shouldShowRoomRedPacketEntry(normalizedPacket)) {
|
|
return;
|
|
}
|
|
final packetId = normalizedPacket.packetId.trim();
|
|
if (packetId.isNotEmpty &&
|
|
packets.any((item) => item.packetId.trim() == packetId)) {
|
|
return;
|
|
}
|
|
packets.add(normalizedPacket);
|
|
}
|
|
|
|
bool _shouldShowRoomRedPacketEntry(RoomRedPacketUiData item) {
|
|
final normalizedStatus = item.statusDesc.toLowerCase();
|
|
final explicitSoldOut =
|
|
item.status == 2 ||
|
|
normalizedStatus.contains('finish') ||
|
|
normalizedStatus.contains('sold');
|
|
return !item.grabbed && !item.isExpired && !explicitSoldOut;
|
|
}
|