mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 19:50:28 +00:00
feat: UI update
This commit is contained in:
parent
2ffb02e62d
commit
6fda5628ff
@ -28,9 +28,10 @@ class S {
|
||||
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||
|
||||
static Future<S> load(Locale locale) {
|
||||
final name = (locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final name =
|
||||
(locale.countryCode?.isEmpty ?? false)
|
||||
? locale.languageCode
|
||||
: locale.toString();
|
||||
final localeName = Intl.canonicalizedLocale(name);
|
||||
return initializeMessages(localeName).then((_) {
|
||||
Intl.defaultLocale = localeName;
|
||||
|
||||
@ -889,4 +889,8 @@ class PartyRoom extends _$PartyRoom {
|
||||
_stopEventStream();
|
||||
_confBox?.close();
|
||||
}
|
||||
|
||||
common.Tag? getMainTagById(String mainTagId) {
|
||||
return state.room.tags[mainTagId];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/app.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/provider/party_room.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/party_room_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/utils/party_room_utils.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
class UserAvatarWidget extends HookConsumerWidget {
|
||||
@ -18,12 +19,12 @@ class UserAvatarWidget extends HookConsumerWidget {
|
||||
final isLoggedIn = partyRoomState.auth.isLoggedIn;
|
||||
final userName = partyRoomState.auth.userInfo?.gameUserId ?? S.current.user_not_logged_in;
|
||||
final avatarUrl = partyRoomState.auth.userInfo?.avatarUrl;
|
||||
final enlistedDate = partyRoomState.auth.userInfo?.enlistedDate;
|
||||
final fullAvatarUrl = (avatarUrl != null && avatarUrl.isNotEmpty) ? '${URLConf.rsiAvatarBaseUrl}$avatarUrl' : null;
|
||||
final uuid = ref.read(appGlobalModelProvider).deviceUUID;
|
||||
return HoverButton(
|
||||
onPressed: () {
|
||||
if (isLoggedIn) {
|
||||
_showAccountCard(context, ref, userName, fullAvatarUrl, uuid);
|
||||
_showAccountCard(context, ref, userName, fullAvatarUrl, enlistedDate);
|
||||
} else {
|
||||
onTapNavigateToPartyRoom();
|
||||
}
|
||||
@ -75,7 +76,7 @@ class UserAvatarWidget extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _showAccountCard(BuildContext context, WidgetRef ref, String userName, String? avatarUrl, String? uuid) {
|
||||
void _showAccountCard(BuildContext context, WidgetRef ref, String userName, String? avatarUrl, Int64? enlistedDate) {
|
||||
final targetContext = context;
|
||||
final box = targetContext.findRenderObject() as RenderBox?;
|
||||
final offset = box?.localToGlobal(Offset.zero) ?? Offset.zero;
|
||||
@ -139,7 +140,7 @@ class UserAvatarWidget extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'$uuid',
|
||||
'注册时间:${PartyRoomUtils.formatDateTime(enlistedDate)}',
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
],
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:local_hero/local_hero.dart';
|
||||
import 'package:starcitizen_doctor/provider/party_room.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/party_room_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/party_room/widgets/party_room_connect_page.dart';
|
||||
@ -27,14 +26,22 @@ class PartyRoomUI extends HookConsumerWidget {
|
||||
widget = PartyRoomDetailPage();
|
||||
}
|
||||
|
||||
return LocalHeroScope(
|
||||
duration: Duration(milliseconds: 180),
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 230),
|
||||
switchInCurve: Curves.easeInOut,
|
||||
switchOutCurve: Curves.easeInOut,
|
||||
child: widget,
|
||||
),
|
||||
return AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 230),
|
||||
switchInCurve: Curves.easeInOut,
|
||||
switchOutCurve: Curves.easeInOut,
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
final offsetAnimation = Tween<Offset>(
|
||||
begin: const Offset(0, 0.08),
|
||||
end: Offset.zero,
|
||||
).chain(CurveTween(curve: Curves.easeInOut)).animate(animation);
|
||||
|
||||
return SlideTransition(
|
||||
position: offsetAnimation,
|
||||
child: FadeTransition(opacity: animation, child: child),
|
||||
);
|
||||
},
|
||||
child: widget,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
22
lib/ui/party_room/utils/party_room_utils.dart
Normal file
22
lib/ui/party_room/utils/party_room_utils.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class PartyRoomUtils {
|
||||
static final DateFormat dateTimeFormatter = DateFormat('yyyy-MM-dd HH:mm:ss');
|
||||
|
||||
/// rpc Int64 时间戳 转 DateTime
|
||||
static DateTime? getDateTime(Int64? timestamp) {
|
||||
if (timestamp == null || timestamp == Int64.ZERO) {
|
||||
return null;
|
||||
}
|
||||
return DateTime.fromMillisecondsSinceEpoch(timestamp.toInt() * 1000);
|
||||
}
|
||||
|
||||
static String? formatDateTime(Int64? timestamp) {
|
||||
final dateTime = getDateTime(timestamp);
|
||||
if (dateTime == null) {
|
||||
return null;
|
||||
}
|
||||
return dateTimeFormatter.format(dateTime);
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,7 @@ class CreateRoomDialog extends HookConsumerWidget {
|
||||
final selectedMainTagData = selectedMainTag.value != null ? partyRoomState.room.tags[selectedMainTag.value] : null;
|
||||
|
||||
return ContentDialog(
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.4),
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.5),
|
||||
title: const Text('创建房间'),
|
||||
content: SizedBox(
|
||||
child: SingleChildScrollView(
|
||||
@ -55,11 +55,14 @@ class CreateRoomDialog extends HookConsumerWidget {
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
Text(tag.name),
|
||||
Text(tag.name, style: TextStyle(fontSize: 16)),
|
||||
if (tag.info.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Text(tag.info, style: TextStyle(fontSize: 11, color: Colors.grey[100])),
|
||||
child: Text(
|
||||
tag.info,
|
||||
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .7)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -99,11 +102,14 @@ class CreateRoomDialog extends HookConsumerWidget {
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
Text(subTag.name),
|
||||
Text(subTag.name, style: TextStyle(fontSize: 16)),
|
||||
if (subTag.info.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Text(subTag.info, style: TextStyle(fontSize: 11, color: Colors.grey[100])),
|
||||
child: Text(
|
||||
subTag.info,
|
||||
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .7)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -159,7 +165,11 @@ class CreateRoomDialog extends HookConsumerWidget {
|
||||
|
||||
InfoLabel(
|
||||
label: '社交链接 (可选)',
|
||||
child: TextBox(controller: socialLinksController, placeholder: 'https://discord.gg/xxxxx', maxLines: 1),
|
||||
child: TextBox(
|
||||
controller: socialLinksController,
|
||||
placeholder: '以 https:// 开头,目前仅支持 qq、discord、kook、oopz 链接',
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:local_hero/local_hero.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/generated/proto/partroom/partroom.pb.dart';
|
||||
import 'package:starcitizen_doctor/provider/party_room.dart';
|
||||
@ -246,7 +245,6 @@ class PartyRoomMemberItem extends ConsumerWidget {
|
||||
child: CacheNetImage(url: avatarUrl),
|
||||
),
|
||||
);
|
||||
if (isOwner) return LocalHero(tag: 'party_room_detail_hero', child: avatarWidget);
|
||||
return avatarWidget;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:animate_do/animate_do.dart';
|
||||
import 'package:extended_image/extended_image.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||
import 'package:hexcolor/hexcolor.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:local_hero/local_hero.dart';
|
||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||
import 'package:starcitizen_doctor/generated/proto/partroom/partroom.pb.dart';
|
||||
import 'package:starcitizen_doctor/provider/party_room.dart';
|
||||
@ -105,17 +106,17 @@ class PartyRoomListPage extends HookConsumerWidget {
|
||||
);
|
||||
final avatarUrl = owner.avatarUrl.isNotEmpty ? '${URLConf.rsiAvatarBaseUrl}${owner.avatarUrl}' : '';
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Tooltip(
|
||||
message: '返回当前房间',
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
ref.read(partyRoomUIModelProvider.notifier).setMinimized(false);
|
||||
},
|
||||
child: LocalHero(
|
||||
tag: 'party_room_detail_hero',
|
||||
return Bounce(
|
||||
duration: Duration(milliseconds: 230),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
alignment: Alignment.bottomRight,
|
||||
child: Tooltip(
|
||||
message: '返回当前房间',
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
ref.read(partyRoomUIModelProvider.notifier).setMinimized(false);
|
||||
},
|
||||
child: Container(
|
||||
width: 56,
|
||||
height: 56,
|
||||
@ -196,13 +197,22 @@ class PartyRoomListPage extends HookConsumerWidget {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(FluentIcons.room, size: 48, color: Colors.grey.withValues(alpha: 0.6)),
|
||||
Icon(FluentIcons.room, size: 48, color: Colors.white.withValues(alpha: 0.6)),
|
||||
const SizedBox(height: 16),
|
||||
Text('暂无房间', style: TextStyle(color: Colors.white.withValues(alpha: 0.7))),
|
||||
Text(
|
||||
uiState.searchOwnerName.isNotEmpty
|
||||
? '没有找到符合条件的房间'
|
||||
: uiState.selectedMainTagId != null
|
||||
? '当前分类下没有房间'
|
||||
: '暂无可用房间',
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: 0.7)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text('成为第一个创建房间的人吧!', style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.5))),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton(onPressed: () => _showCreateRoomDialog(context, ref), child: const Text('创建房间')),
|
||||
if (uiState.searchOwnerName.isEmpty) ...[
|
||||
Text('成为第一个创建房间的人吧!', style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.5))),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton(onPressed: () => _showCreateRoomDialog(context, ref), child: const Text('创建房间')),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -237,11 +247,10 @@ class PartyRoomListPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRoomCard(BuildContext context, WidgetRef ref, PartyRoom partyRoom, dynamic room, int index) {
|
||||
Widget _buildRoomCard(BuildContext context, WidgetRef ref, PartyRoom partyRoom, RoomListItem room, int index) {
|
||||
final avatarUrl = room.ownerAvatar.isNotEmpty ? '${URLConf.rsiAvatarBaseUrl}${room.ownerAvatar}' : '';
|
||||
final partyRoomState = ref.watch(partyRoomProvider);
|
||||
final isCurrentRoom = partyRoomState.room.isInRoom && partyRoomState.room.roomUuid == room.roomUuid;
|
||||
|
||||
return GridItemAnimator(
|
||||
index: index,
|
||||
child: GestureDetector(
|
||||
@ -338,18 +347,7 @@ class PartyRoomListPage extends HookConsumerWidget {
|
||||
spacing: 6,
|
||||
runSpacing: 6,
|
||||
children: [
|
||||
if (room.mainTagId.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF4A9EFF).withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
room.mainTagId,
|
||||
style: const TextStyle(fontSize: 11, color: Color(0xFF4A9EFF)),
|
||||
),
|
||||
),
|
||||
makeTagContainer(partyRoom, room),
|
||||
if (room.socialLinks.isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
@ -382,6 +380,26 @@ class PartyRoomListPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeTagContainer(PartyRoom partyRoom, RoomListItem room) {
|
||||
final tag = partyRoom.getMainTagById(room.mainTagId);
|
||||
final subTag = tag?.subTags.where((e) => e.id == room.subTagId).firstOrNull;
|
||||
|
||||
Widget buildContainer(String name, Color color) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(4)),
|
||||
child: Text(name, style: const TextStyle(fontSize: 11, color: Colors.white)),
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
buildContainer(tag?.name ?? "<${tag?.id}>", HexColor(tag?.color ?? "#0096FF")),
|
||||
if (subTag != null) ...[const SizedBox(width: 4), buildContainer(subTag.name, HexColor(subTag.color))],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showCreateRoomDialog(BuildContext context, WidgetRef ref) async {
|
||||
await showDialog(context: context, builder: (context) => const CreateRoomDialog());
|
||||
}
|
||||
|
||||
16
pubspec.lock
16
pubspec.lock
@ -33,6 +33,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.7"
|
||||
animate_do:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: animate_do
|
||||
sha256: e5c8b92e8495cba5adfff17c0b017d50f46b2766226e9faaf68bc08c91aef034
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
archive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -830,14 +838,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
local_hero:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: local_hero
|
||||
sha256: "5c85451dd51ecd0e8d3656775fac9a6db82f296f200d9931217186d34fed6089"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -36,7 +36,7 @@ dependencies:
|
||||
markdown: ^7.3.0
|
||||
markdown_widget: ^2.3.2+8
|
||||
extended_image: ^10.0.1
|
||||
local_hero: ^0.3.0
|
||||
animate_do: ^4.2.0
|
||||
device_info_plus: ^12.2.0
|
||||
file_picker: ^10.3.6
|
||||
file_sizes: ^1.0.6
|
||||
|
||||
Loading…
Reference in New Issue
Block a user