303 lines
8.9 KiB
Dart
303 lines
8.9 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:yumi/shared/tools/sc_cp_relation_pair_utils.dart';
|
|
import 'package:yumi/services/audio/rtc_manager.dart';
|
|
import 'package:yumi/ui_kit/widgets/room/seat/room_mic_relation_effect.dart';
|
|
|
|
import '../../../../modules/room/seat/sc_seat_item.dart';
|
|
|
|
class RoomSeatWidget extends StatefulWidget {
|
|
const RoomSeatWidget({super.key});
|
|
|
|
@override
|
|
State<RoomSeatWidget> createState() => _RoomSeatWidgetState();
|
|
}
|
|
|
|
class _RoomSeatWidgetState extends State<RoomSeatWidget> {
|
|
static const int _defaultVoiceRoomSeatCount = 10;
|
|
static const int _seatColumnCount = 5;
|
|
static const double _relationEffectWidth = 108;
|
|
static const double _relationEffectHeight = 54;
|
|
static const double _relationEffectTop = -2;
|
|
|
|
int _lastSeatCount = 0;
|
|
|
|
int _normalizeSeatCount(int? seatCount) {
|
|
switch (seatCount) {
|
|
case 5:
|
|
case 10:
|
|
case 15:
|
|
case 20:
|
|
return seatCount ?? 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int _resolvedSeatCount(_RoomSeatLayoutSnapshot snapshot) {
|
|
final int liveSeatCount = _normalizeSeatCount(snapshot.seatCount);
|
|
final int configuredSeatCount = _normalizeSeatCount(
|
|
snapshot.configuredSeatCount,
|
|
);
|
|
final int seatCount =
|
|
configuredSeatCount > 0 ? configuredSeatCount : liveSeatCount;
|
|
if (!snapshot.isExitingCurrentVoiceRoomSession && seatCount > 0) {
|
|
_lastSeatCount = seatCount;
|
|
}
|
|
if (snapshot.isExitingCurrentVoiceRoomSession && _lastSeatCount > 0) {
|
|
return _lastSeatCount;
|
|
}
|
|
if (snapshot.hasCurrentRoom && seatCount == 0) {
|
|
return _lastSeatCount > 0 ? _lastSeatCount : _defaultVoiceRoomSeatCount;
|
|
}
|
|
return seatCount;
|
|
}
|
|
|
|
int _seatCountFromMicIndexes(Iterable<num> micIndexes) {
|
|
var maxIndex = -1;
|
|
var count = 0;
|
|
for (final micIndex in micIndexes) {
|
|
count += 1;
|
|
final resolvedIndex = micIndex.toInt();
|
|
if (resolvedIndex > maxIndex) {
|
|
maxIndex = resolvedIndex;
|
|
}
|
|
}
|
|
final indexedSeatCount = _normalizeSeatCount(maxIndex + 1);
|
|
if (indexedSeatCount > 0) {
|
|
return indexedSeatCount;
|
|
}
|
|
return _normalizeSeatCount(count);
|
|
}
|
|
|
|
int _seatContentVersion(RtcProvider provider) {
|
|
var version = provider.roomWheatMap.length;
|
|
final micIndexes =
|
|
provider.roomWheatMap.keys.toList()..sort((a, b) => a.compareTo(b));
|
|
for (final micIndex in micIndexes) {
|
|
final seat = provider.micAtIndexForDisplay(micIndex);
|
|
final user = seat?.user;
|
|
version = Object.hash(
|
|
version,
|
|
micIndex,
|
|
seat?.micLock ?? false,
|
|
seat?.micMute ?? false,
|
|
user?.id ?? "",
|
|
user?.userAvatar ?? "",
|
|
user?.userNickname ?? "",
|
|
user?.roles ?? "",
|
|
user?.getHeaddress()?.sourceUrl ?? "",
|
|
user?.getHeaddress()?.cover ?? "",
|
|
user?.getVIP()?.name ?? "",
|
|
scCpProfileRelationVersion(user),
|
|
);
|
|
}
|
|
return version;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Selector<RtcProvider, _RoomSeatLayoutSnapshot>(
|
|
selector:
|
|
(context, provider) => _RoomSeatLayoutSnapshot(
|
|
seatCount: _seatCountFromMicIndexes(provider.roomWheatMap.keys),
|
|
seatContentVersion: _seatContentVersion(provider),
|
|
showRoomVisualEffects: provider.shouldShowRoomVisualEffects,
|
|
configuredSeatCount:
|
|
provider.currenRoom?.roomProfile?.roomSetting?.mikeSize
|
|
?.toInt() ??
|
|
provider.previewRoomSeatCount ??
|
|
0,
|
|
hasCurrentRoom: provider.currenRoom != null,
|
|
isExitingCurrentVoiceRoomSession:
|
|
provider.isExitingCurrentVoiceRoomSession,
|
|
),
|
|
builder: (context, snapshot, child) {
|
|
final int seatCount = _resolvedSeatCount(snapshot);
|
|
return seatCount == 5
|
|
? _buildSeat5()
|
|
: (seatCount == 10
|
|
? _buildSeat10()
|
|
: (seatCount == 15
|
|
? _buildSeat15()
|
|
: (seatCount == 20
|
|
? _buildSeat20()
|
|
: Container(height: 180.w))));
|
|
},
|
|
);
|
|
}
|
|
|
|
_buildSeat5() {
|
|
return Container(
|
|
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildSeatRow(const [0, 1, 2, 3, 4]),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
_buildSeat10() {
|
|
return Container(
|
|
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildSeatRow(const [0, 1, 2, 3, 4]),
|
|
_buildSeatRow(const [5, 6, 7, 8, 9]),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
_buildSeat15() {
|
|
return Container(
|
|
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildSeatRow(const [0, 1, 2, 3, 4]),
|
|
_buildSeatRow(const [5, 6, 7, 8, 9]),
|
|
_buildSeatRow(const [10, 11, 12, 13, 14]),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
_buildSeat20() {
|
|
return Container(
|
|
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildSeatRow(const [0, 1, 2, 3, 4]),
|
|
_buildSeatRow(const [5, 6, 7, 8, 9]),
|
|
_buildSeatRow(const [10, 11, 12, 13, 14]),
|
|
_buildSeatRow(const [15, 16, 17, 18, 19]),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSeatRow(List<int> indexes) {
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final rowWidth = constraints.maxWidth;
|
|
final columnWidth = rowWidth / _seatColumnCount;
|
|
return Stack(
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
for (final index in indexes)
|
|
Expanded(flex: 1, child: SCSeatItem(index: index)),
|
|
],
|
|
),
|
|
if (rowWidth.isFinite)
|
|
..._buildRelationEffectsForRow(
|
|
context,
|
|
indexes: indexes,
|
|
columnWidth: columnWidth,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
List<Widget> _buildRelationEffectsForRow(
|
|
BuildContext context, {
|
|
required List<int> indexes,
|
|
required double columnWidth,
|
|
}) {
|
|
final provider = context.read<RtcProvider>();
|
|
if (!provider.shouldShowRoomVisualEffects) {
|
|
return const <Widget>[];
|
|
}
|
|
final effects = <Widget>[];
|
|
for (var slot = 0; slot < indexes.length - 1; slot += 1) {
|
|
final relationInfo = _relationInfoBetweenSeats(
|
|
provider,
|
|
indexes[slot],
|
|
indexes[slot + 1],
|
|
);
|
|
if (relationInfo == null) {
|
|
continue;
|
|
}
|
|
final left = ((slot + 1) * columnWidth - (_relationEffectWidth.w / 2))
|
|
.clamp(0.0, double.infinity);
|
|
effects.add(
|
|
Positioned(
|
|
left: left,
|
|
top: _relationEffectTop.w,
|
|
width: _relationEffectWidth.w,
|
|
height: _relationEffectHeight.w,
|
|
child: IgnorePointer(
|
|
child: RoomMicRelationEffect(
|
|
relationType: relationInfo.relationType,
|
|
cpLevel: relationInfo.cpLevel,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return effects;
|
|
}
|
|
|
|
SCCpRelationPairInfo? _relationInfoBetweenSeats(
|
|
RtcProvider provider,
|
|
int leftIndex,
|
|
int rightIndex,
|
|
) {
|
|
final leftUser = provider.micAtIndexForDisplay(leftIndex)?.user;
|
|
final rightUser = provider.micAtIndexForDisplay(rightIndex)?.user;
|
|
return scCpRelationInfoBetweenProfiles(leftUser, rightUser);
|
|
}
|
|
}
|
|
|
|
class _RoomSeatLayoutSnapshot {
|
|
const _RoomSeatLayoutSnapshot({
|
|
required this.seatCount,
|
|
required this.seatContentVersion,
|
|
required this.showRoomVisualEffects,
|
|
required this.configuredSeatCount,
|
|
required this.hasCurrentRoom,
|
|
required this.isExitingCurrentVoiceRoomSession,
|
|
});
|
|
|
|
final int seatCount;
|
|
final int seatContentVersion;
|
|
final bool showRoomVisualEffects;
|
|
final int configuredSeatCount;
|
|
final bool hasCurrentRoom;
|
|
final bool isExitingCurrentVoiceRoomSession;
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) {
|
|
return true;
|
|
}
|
|
return other is _RoomSeatLayoutSnapshot &&
|
|
other.seatCount == seatCount &&
|
|
other.seatContentVersion == seatContentVersion &&
|
|
other.showRoomVisualEffects == showRoomVisualEffects &&
|
|
other.configuredSeatCount == configuredSeatCount &&
|
|
other.hasCurrentRoom == hasCurrentRoom &&
|
|
other.isExitingCurrentVoiceRoomSession ==
|
|
isExitingCurrentVoiceRoomSession;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => Object.hash(
|
|
seatCount,
|
|
seatContentVersion,
|
|
showRoomVisualEffects,
|
|
configuredSeatCount,
|
|
hasCurrentRoom,
|
|
isExitingCurrentVoiceRoomSession,
|
|
);
|
|
}
|