mirror of
https://ghfast.top/https://github.com/StarCitizenToolBox/app.git
synced 2025-06-28 11:24:46 +08:00
[Party Room] 加入房间功能
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/grpc/party_room_server.dart';
|
||||
import 'package:starcitizen_doctor/generated/grpc/party_room_server/index.pb.dart';
|
||||
import 'package:starcitizen_doctor/global_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/grpc/party_room_server.dart';
|
||||
|
||||
class PartyRoomCreateDialogUIModel extends BaseUIModel {
|
||||
Map<String?, RoomType> roomTypes;
|
||||
|
148
lib/ui/party_room/party_room_chat_ui.dart
Normal file
148
lib/ui/party_room/party_room_chat_ui.dart
Normal file
@ -0,0 +1,148 @@
|
||||
import 'package:starcitizen_doctor/base/ui.dart';
|
||||
import 'package:starcitizen_doctor/generated/grpc/party_room_server/index.pb.dart';
|
||||
import 'package:starcitizen_doctor/widgets/cache_image.dart';
|
||||
|
||||
import 'party_room_chat_ui_model.dart';
|
||||
|
||||
class PartyRoomChatUI extends BaseUI<PartyRoomChatUIModel> {
|
||||
@override
|
||||
Widget? buildBody(BuildContext context, PartyRoomChatUIModel model) {
|
||||
final roomData = model.serverResultRoomData;
|
||||
if (roomData == null) return makeLoading(context);
|
||||
final typesMap = model.partyRoomHomeUIModel.roomTypes;
|
||||
final title =
|
||||
"${roomData.owner} 的 ${typesMap?[roomData.roomTypeID]?.name ?? roomData.roomTypeID}房间";
|
||||
// final createTime =
|
||||
// DateTime.fromMillisecondsSinceEpoch(roomData.createTime.toInt());
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(color: Colors.black.withOpacity(.25)),
|
||||
child: makeTitleBar(model, title, roomData),
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 220,
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
decoration: BoxDecoration(color: Colors.black.withOpacity(.07)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
makeItemRow("玩家数量:",
|
||||
"${model.playersMap?.length ?? 0} / ${roomData.maxPlayer}"),
|
||||
const SizedBox(height: 12),
|
||||
if (model.playersMap == null)
|
||||
Expanded(child: makeLoading(context))
|
||||
else
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = model.playersMap!.entries
|
||||
.elementAt(index)
|
||||
.value;
|
||||
return Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
child: CacheNetImage(
|
||||
url: item.avatar,
|
||||
width: 28,
|
||||
height: 28,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Expanded(child: Text(item.playerName)),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 3, bottom: 3, left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green,
|
||||
borderRadius:
|
||||
BorderRadius.circular(1000)),
|
||||
child: Text(
|
||||
"${model.playerStatusMap[item.status] ?? item.status}",
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
itemCount: model.playersMap!.length,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeItemRow(String title, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 1, bottom: 1),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(color: Colors.white.withOpacity(.6)),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeTitleBar(
|
||||
PartyRoomChatUIModel model, String title, RoomData roomData) {
|
||||
return Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
child: CacheNetImage(
|
||||
url: roomData.avatar,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 18),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 3, bottom: 3, left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green, borderRadius: BorderRadius.circular(1000)),
|
||||
child: Text(
|
||||
"${model.partyRoomHomeUIModel.roomStatus[roomData.status]}")),
|
||||
const SizedBox(width: 12),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
FluentIcons.leave,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => model.onClose())
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String getUITitle(BuildContext context, PartyRoomChatUIModel model) => "Chat";
|
||||
}
|
93
lib/ui/party_room/party_room_chat_ui_model.dart
Normal file
93
lib/ui/party_room/party_room_chat_ui_model.dart
Normal file
@ -0,0 +1,93 @@
|
||||
import 'package:grpc/grpc.dart';
|
||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/grpc/party_room_server.dart';
|
||||
import 'package:starcitizen_doctor/generated/grpc/party_room_server/index.pb.dart';
|
||||
import 'package:starcitizen_doctor/global_ui_model.dart';
|
||||
|
||||
import 'party_room_home_ui_model.dart';
|
||||
|
||||
class PartyRoomChatUIModel extends BaseUIModel {
|
||||
PartyRoomHomeUIModel partyRoomHomeUIModel;
|
||||
|
||||
PartyRoomChatUIModel(this.partyRoomHomeUIModel);
|
||||
|
||||
RoomData? selectRoom;
|
||||
|
||||
RoomData? serverResultRoomData;
|
||||
|
||||
ResponseStream<RoomUpdateMessage>? roomStream;
|
||||
|
||||
Map<String, RoomUserData>? playersMap;
|
||||
|
||||
setRoom(RoomData? selectRoom) {
|
||||
if (this.selectRoom == null) {
|
||||
this.selectRoom = selectRoom;
|
||||
notifyListeners();
|
||||
loadRoom();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
final playerStatusMap = {
|
||||
RoomUserStatus.RoomUserStatusJoin: "在线",
|
||||
RoomUserStatus.RoomUserStatusLostOffline: "离线",
|
||||
RoomUserStatus.RoomUserStatusLeave: "已离开",
|
||||
RoomUserStatus.RoomUserStatusWaitingConnect: "正在连接...",
|
||||
};
|
||||
|
||||
onClose() async {
|
||||
final ok = await showConfirmDialogs(
|
||||
context!, "确认离开房间?", const Text("离开房间后,您的位置将被释放。"));
|
||||
if (ok == true) {
|
||||
partyRoomHomeUIModel.pageCtrl.animateToPage(0,
|
||||
duration: const Duration(milliseconds: 130),
|
||||
curve: Curves.easeInOutSine);
|
||||
disposeRoom();
|
||||
}
|
||||
}
|
||||
|
||||
loadRoom() async {
|
||||
if (selectRoom == null) return;
|
||||
final userName = await globalUIModel.getRunningGameUser();
|
||||
if (userName == null) return;
|
||||
roomStream = PartyRoomGrpcServer.joinRoom(
|
||||
selectRoom!.id, userName, AppConf.deviceUUID);
|
||||
roomStream!.listen((value) {
|
||||
dPrint("PartyRoomChatUIModel.roomStream.listen === $value");
|
||||
if (value.roomUpdateType == RoomUpdateType.RoomClose) {
|
||||
} else if (value.roomUpdateType == RoomUpdateType.RoomUpdateData) {
|
||||
if (value.hasRoomData()) {
|
||||
serverResultRoomData = value.roomData;
|
||||
}
|
||||
if (value.usersData.isNotEmpty) {
|
||||
_updatePlayerList(value.usersData);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
})
|
||||
..onError((err) {
|
||||
// showToast(context!, "连接到服务器出现错误:$err");
|
||||
dPrint("PartyRoomChatUIModel.roomStream onError $err");
|
||||
})
|
||||
..onDone(() {
|
||||
// showToast(context!, "房间已关闭");
|
||||
dPrint("PartyRoomChatUIModel.roomStream onDone");
|
||||
});
|
||||
}
|
||||
|
||||
disposeRoom() {
|
||||
selectRoom = null;
|
||||
roomStream?.cancel();
|
||||
roomStream = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _updatePlayerList(List<RoomUserData> usersData) {
|
||||
playersMap ??= {};
|
||||
for (var element in usersData) {
|
||||
playersMap![element.playerName] = element;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
@ -4,13 +4,40 @@ import 'dart:ui';
|
||||
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||
import 'package:starcitizen_doctor/base/ui.dart';
|
||||
import 'package:starcitizen_doctor/generated/grpc/party_room_server/index.pb.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/party_room_chat_ui.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/party_room_chat_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/cache_image.dart';
|
||||
|
||||
import 'party_room_home_ui_model.dart';
|
||||
|
||||
class PartyRoomHomeUI extends BaseUI<PartyRoomHomeUIModel> {
|
||||
@override
|
||||
void initState() {
|
||||
Future.delayed(const Duration(milliseconds: 16)).then((_) {
|
||||
ref.watch(provider).checkUIInit();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final model = ref.watch(provider);
|
||||
return PageView(
|
||||
controller: model.pageCtrl,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
super.build(context),
|
||||
BaseUIContainer(
|
||||
uiCreate: () => PartyRoomChatUI(),
|
||||
modelCreate: () =>
|
||||
model.getChildUIModelProviders<PartyRoomChatUIModel>("chat"))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget? buildBody(BuildContext context, PartyRoomHomeUIModel model) {
|
||||
if (model.pingServerMessage == null) return makeLoading(context);
|
||||
@ -40,99 +67,7 @@ class PartyRoomHomeUI extends BaseUI<PartyRoomHomeUIModel> {
|
||||
crossAxisSpacing: 24,
|
||||
itemCount: model.rooms!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = model.rooms![index];
|
||||
final itemType = model.roomTypes?[item.roomTypeID];
|
||||
final itemSubTypes = {
|
||||
for (var t in itemType?.subTypes ?? <RoomSubtype>[]) t.id: t
|
||||
};
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
image: DecorationImage(
|
||||
image: ExtendedNetworkImageProvider(item.avatar,
|
||||
cache: true),
|
||||
fit: BoxFit.cover)),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(.4),
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16, right: 16, top: 12, bottom: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"${itemType?.name ?? item.roomTypeID}房间",
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 3,
|
||||
bottom: 3,
|
||||
left: 12,
|
||||
right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green,
|
||||
borderRadius:
|
||||
BorderRadius.circular(1000)),
|
||||
child: Text(
|
||||
"${model.roomStatus[item.status]}")),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
makeItemRow("房主:", item.owner),
|
||||
makeItemRow(
|
||||
"当前人数:", "${item.curPlayer}"),
|
||||
makeItemRow(
|
||||
"最大人数:", "${item.maxPlayer}"),
|
||||
],
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
child: CacheNetImage(
|
||||
url: item.avatar,
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
for (var value in item.roomSubTypeIds)
|
||||
makeSubTypeTag(
|
||||
value, model, itemSubTypes),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return makeRoomItemWidget(context, model, index);
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -141,6 +76,105 @@ class PartyRoomHomeUI extends BaseUI<PartyRoomHomeUIModel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeRoomItemWidget(
|
||||
BuildContext context,
|
||||
PartyRoomHomeUIModel model,
|
||||
int index,
|
||||
) {
|
||||
final item = model.rooms![index];
|
||||
final itemType = model.roomTypes?[item.roomTypeID];
|
||||
final itemSubTypes = {
|
||||
for (var t in itemType?.subTypes ?? <RoomSubtype>[]) t.id: t
|
||||
};
|
||||
final createTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(item.createTime.toInt());
|
||||
return Tilt(
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
image: DecorationImage(
|
||||
image: ExtendedNetworkImageProvider(item.avatar, cache: true),
|
||||
fit: BoxFit.cover)),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(.4),
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(13),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16, right: 16, top: 12, bottom: 12),
|
||||
child: GestureDetector(
|
||||
onTap: () => model.onTapRoom(item),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
"${itemType?.name ?? item.roomTypeID}房间",
|
||||
style: const TextStyle(fontSize: 20),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 3, bottom: 3, left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green,
|
||||
borderRadius: BorderRadius.circular(1000)),
|
||||
child: Text("${model.roomStatus[item.status]}")),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
makeItemRow("房主:", item.owner),
|
||||
makeItemRow("玩家数量:",
|
||||
"${item.curPlayer} / ${item.maxPlayer}"),
|
||||
makeItemRow("创建时间:", "${createTime.toLocal()}"),
|
||||
],
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
child: CacheNetImage(
|
||||
url: item.avatar,
|
||||
width: 64,
|
||||
height: 64,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
for (var value in item.roomSubTypeIds)
|
||||
makeSubTypeTag(value, model, itemSubTypes),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeSubTypeTag(String id, PartyRoomHomeUIModel model,
|
||||
Map<String, RoomSubtype> itemSubTypes) {
|
||||
final name = itemSubTypes[id]?.name ?? id;
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:starcitizen_doctor/base/ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/grpc/party_room_server.dart';
|
||||
import 'package:starcitizen_doctor/generated/grpc/party_room_server/index.pb.dart';
|
||||
import 'package:starcitizen_doctor/grpc/party_room_server.dart';
|
||||
import 'package:starcitizen_doctor/global_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/dialogs/party_room_create_dialog_ui_model.dart';
|
||||
|
||||
import 'dialogs/party_room_create_dialog_ui.dart';
|
||||
import 'party_room_chat_ui_model.dart';
|
||||
|
||||
class PartyRoomHomeUIModel extends BaseUIModel {
|
||||
String? pingServerMessage;
|
||||
@ -40,10 +43,22 @@ class PartyRoomHomeUIModel extends BaseUIModel {
|
||||
|
||||
List<RoomData>? rooms;
|
||||
|
||||
final pageCtrl = PageController();
|
||||
|
||||
@override
|
||||
void initModel() {
|
||||
super.initModel();
|
||||
_loadTypes();
|
||||
_touchUser();
|
||||
}
|
||||
|
||||
@override
|
||||
BaseUIModel? onCreateChildUIModel(modelKey) {
|
||||
switch (modelKey) {
|
||||
case "chat":
|
||||
return PartyRoomChatUIModel(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -61,6 +76,7 @@ class PartyRoomHomeUIModel extends BaseUIModel {
|
||||
pageNum = 0;
|
||||
rooms = null;
|
||||
notifyListeners();
|
||||
_touchUser();
|
||||
return super.reloadData();
|
||||
}
|
||||
|
||||
@ -167,4 +183,33 @@ class PartyRoomHomeUIModel extends BaseUIModel {
|
||||
onRefreshRoom() {
|
||||
reloadData();
|
||||
}
|
||||
|
||||
Future<void> _touchUser() async {
|
||||
if (getCreatedChildUIModel<PartyRoomChatUIModel>("chat")?.selectRoom ==
|
||||
null) {
|
||||
final userName = await globalUIModel.getRunningGameUser();
|
||||
if (userName == null) return;
|
||||
// 检测用户已加入的房间
|
||||
final room = await handleError(() =>
|
||||
PartyRoomGrpcServer.touchUserRoom(userName, AppConf.deviceUUID));
|
||||
dPrint("touch room == ${room?.toProto3Json()}");
|
||||
if (room == null || room.id == "") return;
|
||||
onTapRoom(room);
|
||||
}
|
||||
}
|
||||
|
||||
onTapRoom(RoomData item) {
|
||||
getCreatedChildUIModel<PartyRoomChatUIModel>("chat", create: true)
|
||||
?.setRoom(item);
|
||||
notifyListeners();
|
||||
pageCtrl.animateToPage(1,
|
||||
duration: const Duration(milliseconds: 100), curve: Curves.easeInExpo);
|
||||
}
|
||||
|
||||
void checkUIInit() {
|
||||
if (getCreatedChildUIModel<PartyRoomChatUIModel>("chat")?.selectRoom !=
|
||||
null) {
|
||||
pageCtrl.jumpToPage(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user