feat: L10n for party room

* Initial plan

* Fix l10n_gen multi-line string bug and add localization strings for party_room and splash modules

Co-authored-by: xkeyC <39891083+xkeyC@users.noreply.github.com>

* Fix const/import issues in l10n_replace output

Co-authored-by: xkeyC <39891083+xkeyC@users.noreply.github.com>

* Fix comment issues found in code review

Co-authored-by: xkeyC <39891083+xkeyC@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: xkeyC <39891083+xkeyC@users.noreply.github.com>
This commit is contained in:
Copilot
2025-12-02 12:23:10 +08:00
committed by GitHub
parent 0f24b506fa
commit 6c3d9d05be
24 changed files with 5693 additions and 465 deletions

View File

@@ -232,7 +232,7 @@ class PartyRoomUIModel extends _$PartyRoomUIModel {
/// 完成注册
Future<void> completeRegister() async {
if (state.registerGameUserId.isEmpty) {
throw Exception('游戏ID不能为空');
throw Exception(S.current.party_room_game_id_empty);
}
state = state.copyWith(isLoading: true, errorMessage: null);
@@ -338,4 +338,4 @@ class PartyRoomUIModel extends _$PartyRoomUIModel {
void setMinimized(bool minimized) {
state = state.copyWith(isMinimized: minimized);
}
}
}

View File

@@ -66,7 +66,7 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
}
} catch (e) {
// 游戏未启动或发生错误
state = state.copyWith(location: '<游戏未启动>', gameStartTime: null, kills: 0, deaths: 0);
state = state.copyWith(location: S.current.party_room_game_not_started, gameStartTime: null, kills: 0, deaths: 0);
}
await Future.delayed(const Duration(seconds: 10));
}
@@ -149,4 +149,4 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
var path = p.path;
return path.replaceAll(r"Bin64\StarCitizen.exe", "Game.log");
}
}
}

View File

@@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
import 'package:starcitizen_doctor/generated/proto/partroom/partroom.pb.dart' as partroom;
import 'package:starcitizen_doctor/provider/party_room.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
/// 创建/编辑房间对话框
class CreateRoomDialog extends HookConsumerWidget {
@@ -30,7 +31,7 @@ class CreateRoomDialog extends HookConsumerWidget {
return ContentDialog(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.5),
title: Text(isEdit ? '编辑房间' : '创建房间'),
title: Text(isEdit ? S.current.party_room_edit_room : '创建房间'),
content: SizedBox(
child: SingleChildScrollView(
child: Column(
@@ -40,9 +41,9 @@ class CreateRoomDialog extends HookConsumerWidget {
Column(
children: [
InfoLabel(
label: '房间类型',
label: S.current.party_room_room_type,
child: ComboBox<String>(
placeholder: const Text('选择主标签'),
placeholder: Text(S.current.party_room_select_main_tag),
value: selectedMainTag.value,
isExpanded: true,
items: partyRoomState.room.tags.values.map((tag) {
@@ -84,13 +85,13 @@ class CreateRoomDialog extends HookConsumerWidget {
// 子标签 - 始终显示,避免布局跳动
InfoLabel(
label: '子标签 (可选)',
label: S.current.party_room_sub_tag_optional,
child: ComboBox<String>(
placeholder: const Text('选择子标签'),
placeholder: Text(S.current.party_room_select_sub_tag),
value: selectedSubTag.value,
isExpanded: true,
items: [
const ComboBoxItem(value: null, child: Text('')),
ComboBoxItem(value: null, child: Text(S.current.party_room_none)),
if (selectedMainTagData != null)
...selectedMainTagData.subTags.map((subTag) {
return ComboBoxItem(
@@ -132,10 +133,10 @@ class CreateRoomDialog extends HookConsumerWidget {
),
const SizedBox(height: 16),
InfoLabel(
label: '目标人数 (2-100)',
label: S.current.party_room_target_members,
child: TextBox(
controller: targetMembersController,
placeholder: '输入目标人数',
placeholder: S.current.party_room_enter_target_members,
keyboardType: TextInputType.number,
),
),
@@ -148,21 +149,21 @@ class CreateRoomDialog extends HookConsumerWidget {
onChanged: (value) {
hasPassword.value = value ?? false;
},
content: const Text('设置密码'),
content: Text(S.current.party_room_set_password),
),
],
),
const SizedBox(height: 8),
// 密码输入框 - 始终显示,避免布局跳动
InfoLabel(
label: '房间密码',
label: S.current.party_room_room_password,
child: TextBox(
controller: passwordController,
placeholder: isEdit
? "为空则不更新密码,取消勾选则取消密码"
? S.current.party_room_password_empty_hint
: hasPassword.value
? '输入密码'
: '未启用密码',
? S.current.party_room_enter_password
: S.current.party_room_password_disabled,
obscureText: hasPassword.value,
maxLines: 1,
maxLength: 12,
@@ -172,10 +173,10 @@ class CreateRoomDialog extends HookConsumerWidget {
const SizedBox(height: 16),
],
InfoLabel(
label: '社交链接 (可选)',
label: S.current.party_room_social_links_optional,
child: TextBox(
controller: socialLinksController,
placeholder: '以 https:// 开头,目前仅支持 qq、discord、kook、oopz 链接',
placeholder: S.current.party_room_social_links_placeholder,
maxLines: 1,
),
),
@@ -193,8 +194,8 @@ class CreateRoomDialog extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('提示'),
content: const Text('请选择房间类型'),
title: Text(S.current.app_common_tip),
content: Text(S.current.party_room_select_room_type),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
);
@@ -206,8 +207,8 @@ class CreateRoomDialog extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('提示'),
content: const Text('目标人数必须在 2-100 之间'),
title: Text(S.current.app_common_tip),
content: Text(S.current.party_room_target_members_range),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
);
@@ -219,8 +220,8 @@ class CreateRoomDialog extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('提示'),
content: const Text('请输入密码'),
title: Text(S.current.app_common_tip),
content: Text(S.current.party_room_enter_password_required),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
);
@@ -234,7 +235,7 @@ class CreateRoomDialog extends HookConsumerWidget {
// 检查是否为 https 开头的链接
final invalidLinks = socialLinks.where((link) => !link.startsWith('https://')).toList();
if (invalidLinks.isNotEmpty) {
showToast(context, "链接格式错误!");
showToast(context, S.current.party_room_link_format_error);
return;
}
@@ -270,7 +271,7 @@ class CreateRoomDialog extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: Text(isEdit ? '更新失败' : '创建失败'),
title: Text(isEdit ? S.current.party_room_update_failed : '创建失败'),
content: Text(e.toString()),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
@@ -280,9 +281,9 @@ class CreateRoomDialog extends HookConsumerWidget {
},
child: isCreating.value
? const SizedBox(width: 16, height: 16, child: ProgressRing(strokeWidth: 2))
: Text(isEdit ? '保存' : '创建'),
: Text(isEdit ? S.current.party_room_save : '创建'),
),
Button(onPressed: isCreating.value ? null : () => Navigator.pop(context), child: const Text('取消')),
Button(onPressed: isCreating.value ? null : () => Navigator.pop(context), child: Text(S.current.home_action_cancel)),
],
);
}
@@ -308,4 +309,4 @@ class CreateRoomDialog extends HookConsumerWidget {
return Colors.grey;
}
}
}
}

View File

@@ -36,16 +36,16 @@ class _PartyRoomDetailPageState extends ConsumerState<PartyRoomDetailPage> {
final result = await showBaseDialog(
context,
title: '连接已断开',
content: const Text('与房间服务器的连接已断开,是否重新连接?'),
title: S.current.party_room_disconnected,
content: Text(S.current.party_room_reconnect_prompt),
actions: [
Button(
onPressed: () => Navigator.of(context).pop('leave'),
child: const Padding(padding: EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8), child: Text('退出房间')),
child: Padding(padding: const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8), child: Text(S.current.party_room_exit_room)),
),
FilledButton(
onPressed: () => Navigator.of(context).pop('reconnect'),
child: const Padding(padding: EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8), child: Text('重新连接')),
child: Padding(padding: const EdgeInsets.only(top: 2, bottom: 2, left: 8, right: 8), child: Text(S.current.party_room_reconnect)),
),
],
);
@@ -165,4 +165,4 @@ class _PartyRoomDetailPageState extends ConsumerState<PartyRoomDetailPage> {
),
);
}
}
}

View File

@@ -4,6 +4,7 @@ import 'package:starcitizen_doctor/common/utils/base_utils.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/create_room_dialog.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
/// 房间信息头部组件
class PartyRoomHeader extends ConsumerWidget {
@@ -40,7 +41,7 @@ class PartyRoomHeader extends ConsumerWidget {
const SizedBox(width: 8),
Expanded(
child: Text(
room?.ownerGameId ?? '房间',
room?.ownerGameId ?? S.current.party_room_room,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.white),
overflow: TextOverflow.ellipsis,
),
@@ -70,7 +71,7 @@ class PartyRoomHeader extends ConsumerWidget {
builder: (context) => CreateRoomDialog(roomInfo: room),
);
},
child: const Text('编辑房间'),
child: Text(S.current.party_room_edit_room),
),
),
const SizedBox(width: 8),
@@ -80,13 +81,13 @@ class PartyRoomHeader extends ConsumerWidget {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('确认解散'),
content: const Text('确定要解散房间吗?所有成员将被移出。'),
title: Text(S.current.party_room_confirm_dismiss),
content: Text(S.current.party_room_dismiss_confirm_msg),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(
style: ButtonStyle(backgroundColor: WidgetStateProperty.all(const Color(0xFFDA373C))),
child: const Text('解散', style: TextStyle(color: Colors.white)),
child: Text(S.current.party_room_dismiss, style: TextStyle(color: Colors.white)),
onPressed: () => Navigator.pop(context, true),
),
],
@@ -104,7 +105,7 @@ class PartyRoomHeader extends ConsumerWidget {
return const Color(0xFFDA373C);
}),
),
child: const Text('解散房间', style: TextStyle(color: Colors.white)),
child: Text(S.current.party_room_dismiss_room, style: TextStyle(color: Colors.white)),
),
),
],
@@ -115,7 +116,7 @@ class PartyRoomHeader extends ConsumerWidget {
width: double.infinity,
child: Button(
onPressed: () async {
final userOK = await showConfirmDialogs(context, "提示", Text("确认离开房间吗?"));
final userOK = await showConfirmDialogs(context, S.current.app_common_tip, Text(S.current.party_room_leave_confirm));
if (!userOK) return;
await partyRoom.leaveRoom();
},
@@ -133,7 +134,7 @@ class PartyRoomHeader extends ConsumerWidget {
return const Color(0xFFDBDEE1);
}),
),
child: const Text('离开房间'),
child: Text(S.current.party_room_leave_room),
),
),
],
@@ -141,4 +142,4 @@ class PartyRoomHeader extends ConsumerWidget {
),
);
}
}
}

View File

@@ -5,6 +5,7 @@ import 'package:starcitizen_doctor/generated/proto/partroom/partroom.pb.dart';
import 'package:starcitizen_doctor/provider/party_room.dart';
import 'package:starcitizen_doctor/ui/party_room/utils/party_room_utils.dart';
import 'package:starcitizen_doctor/widgets/src/cache_image.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
/// 成员列表侧边栏
class PartyRoomMemberList extends ConsumerWidget {
@@ -18,7 +19,7 @@ class PartyRoomMemberList extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
if (members.isEmpty) {
return Center(
child: Text('暂无成员', style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 12)),
child: Text(S.current.party_room_no_members, style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 12)),
);
}
@@ -131,7 +132,7 @@ class PartyRoomMemberItem extends ConsumerWidget {
// 复制ID - 所有用户可用
MenuFlyoutItem(
leading: const Icon(FluentIcons.copy, size: 16),
text: const Text('复制游戏ID'),
text: Text(S.current.party_room_copy_game_id),
onPressed: () async {
await Clipboard.setData(ClipboardData(text: member.gameUserId));
},
@@ -144,16 +145,16 @@ class PartyRoomMemberItem extends ConsumerWidget {
const MenuFlyoutSeparator(),
MenuFlyoutItem(
leading: const Icon(FluentIcons.people, size: 16),
text: const Text('转移房主'),
text: Text(S.current.party_room_transfer_owner),
onPressed: () async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('转移房主'),
title: Text(S.current.party_room_transfer_owner),
content: Text('确定要将房主转移给 ${member.handleName.isNotEmpty ? member.handleName : member.gameUserId} 吗?'),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: const Text('转移'), onPressed: () => Navigator.pop(context, true)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: Text(S.current.party_room_transfer), onPressed: () => Navigator.pop(context, true)),
],
),
);
@@ -165,7 +166,7 @@ class PartyRoomMemberItem extends ConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('操作失败'),
title: Text(S.current.party_room_operation_failed),
content: Text('转移房主失败:$e'),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
@@ -177,18 +178,18 @@ class PartyRoomMemberItem extends ConsumerWidget {
),
MenuFlyoutItem(
leading: const Icon(FluentIcons.remove_from_shopping_list, size: 16),
text: const Text('踢出成员'),
text: Text(S.current.party_room_kick_member),
onPressed: () async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('踢出成员'),
title: Text(S.current.party_room_kick_member),
content: Text('确定要踢出 ${member.handleName.isNotEmpty ? member.handleName : member.gameUserId} 吗?'),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(
style: ButtonStyle(backgroundColor: WidgetStateProperty.all(const Color(0xFFDA373C))),
child: const Text('踢出'),
child: Text(S.current.party_room_kick),
onPressed: () => Navigator.pop(context, true),
),
],
@@ -202,7 +203,7 @@ class PartyRoomMemberItem extends ConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('操作失败'),
title: Text(S.current.party_room_operation_failed),
content: Text('踢出成员失败:$e'),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
@@ -244,4 +245,4 @@ class PartyRoomMemberItem extends ConsumerWidget {
);
return avatarWidget;
}
}
}

View File

@@ -7,6 +7,7 @@ import 'package:starcitizen_doctor/ui/party_room/utils/party_room_utils.dart';
import 'package:starcitizen_doctor/widgets/src/cache_image.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter/services.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
/// 消息列表组件
class PartyRoomMessageList extends ConsumerWidget {
@@ -31,7 +32,7 @@ class PartyRoomMessageList extends ConsumerWidget {
children: [
Icon(FluentIcons.chat, size: 64, color: Colors.white.withValues(alpha: .6)),
const SizedBox(height: 16),
Text('暂无消息', style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 14)),
Text(S.current.party_room_no_messages, style: TextStyle(color: Colors.white.withValues(alpha: 0.5), fontSize: 14)),
],
),
);
@@ -79,10 +80,10 @@ class PartyRoomMessageList extends ConsumerWidget {
child: const Icon(FluentIcons.info, size: 14, color: Colors.white),
),
const SizedBox(width: 12),
const Expanded(
Expanded(
child: Text(
'该房间包含第三方社交链接,点击加入自由交流吧~',
style: TextStyle(fontSize: 14, color: Color(0xFFDBDEE1), fontWeight: FontWeight.w500),
S.current.party_room_social_links_hint,
style: const TextStyle(fontSize: 14, color: Color(0xFFDBDEE1), fontWeight: FontWeight.w500),
),
),
],
@@ -137,10 +138,10 @@ class PartyRoomMessageList extends ConsumerWidget {
child: const Icon(FluentIcons.copy, size: 14, color: Colors.white),
),
const SizedBox(width: 12),
const Expanded(
Expanded(
child: Text(
'复制房主的游戏ID可在游戏首页添加好友快速组队',
style: TextStyle(fontSize: 14, color: Color(0xFFDBDEE1), fontWeight: FontWeight.w500),
S.current.party_room_copy_owner_id_hint,
style: const TextStyle(fontSize: 14, color: Color(0xFFDBDEE1), fontWeight: FontWeight.w500),
),
),
],
@@ -170,9 +171,9 @@ class PartyRoomMessageList extends ConsumerWidget {
return const Color(0xFF5865F2);
}),
),
child: const Text(
'复制',
style: TextStyle(fontSize: 13, color: Colors.white, fontWeight: FontWeight.w500),
child: Text(
S.current.support_dev_copy_button,
style: const TextStyle(fontSize: 13, color: Colors.white, fontWeight: FontWeight.w500),
),
),
],
@@ -193,7 +194,7 @@ class PartyRoomMessageList extends ConsumerWidget {
if (link.contains('discord')) return 'Discord';
if (link.contains('kook')) return 'KOOK';
if (link.contains('qq')) return 'QQ';
return '链接';
return S.current.party_room_link;
}
}
@@ -270,8 +271,8 @@ class _MessageItem extends ConsumerWidget {
String userName,
String? avatarUrl,
) {
final location = roomEvent.signalParams['location'] ?? '未知位置';
final area = roomEvent.signalParams['area'] ?? '未知区域';
final location = roomEvent.signalParams['location'] ?? S.current.party_room_unknown_location;
final area = roomEvent.signalParams['area'] ?? S.current.party_room_unknown_area;
return Container(
margin: const EdgeInsets.only(bottom: 16),
@@ -322,18 +323,18 @@ class _MessageItem extends ConsumerWidget {
child: const Icon(FluentIcons.status_error_full, size: 14, color: Color(0xFFED4245)),
),
const SizedBox(width: 8),
const Text(
'玩家死亡',
style: TextStyle(fontSize: 14, color: Color(0xFFED4245), fontWeight: FontWeight.w600),
Text(
S.current.party_room_player_death,
style: const TextStyle(fontSize: 14, color: Color(0xFFED4245), fontWeight: FontWeight.w600),
),
],
),
const SizedBox(height: 12),
// 位置信息
_buildInfoRow(icon: FluentIcons.location, label: '位置', value: location),
_buildInfoRow(icon: FluentIcons.location, label: S.current.party_room_location, value: location),
const SizedBox(height: 8),
// 区域信息
_buildInfoRow(icon: FluentIcons.map_pin, label: '区域', value: area),
_buildInfoRow(icon: FluentIcons.map_pin, label: S.current.party_room_area, value: area),
],
),
),
@@ -365,7 +366,7 @@ class _MessageItem extends ConsumerWidget {
String _getEventUserName(partroom.RoomEvent event) {
switch (event.type) {
case partroom.RoomEventType.SIGNAL_BROADCAST:
return event.signalSender.isNotEmpty ? event.signalSender : '未知用户';
return event.signalSender.isNotEmpty ? event.signalSender : S.current.party_room_unknown_user;
case partroom.RoomEventType.MEMBER_JOINED:
case partroom.RoomEventType.MEMBER_LEFT:
case partroom.RoomEventType.MEMBER_KICKED:
@@ -373,11 +374,11 @@ class _MessageItem extends ConsumerWidget {
? event.member.handleName
: event.hasMember()
? event.member.gameUserId
: '未知用户';
: S.current.party_room_unknown_user;
case partroom.RoomEventType.OWNER_CHANGED:
return event.hasMember() && event.member.handleName.isNotEmpty ? event.member.handleName : '新房主';
return event.hasMember() && event.member.handleName.isNotEmpty ? event.member.handleName : S.current.party_room_new_owner;
default:
return '系统';
return S.current.party_room_system;
}
}
@@ -408,19 +409,19 @@ class _MessageItem extends ConsumerWidget {
}
return signalType?.name ?? event.signalId;
case partroom.RoomEventType.MEMBER_JOINED:
return '加入了房间';
return S.current.party_room_joined_room;
case partroom.RoomEventType.MEMBER_LEFT:
return '离开了房间';
return S.current.party_room_left_room;
case partroom.RoomEventType.OWNER_CHANGED:
return '成为了新房主';
return S.current.party_room_became_owner;
case partroom.RoomEventType.ROOM_UPDATED:
return '房间信息已更新';
return S.current.party_room_info_updated;
case partroom.RoomEventType.MEMBER_STATUS_UPDATED:
return null;
case partroom.RoomEventType.ROOM_DISMISSED:
return '房间已解散';
return S.current.party_room_dismissed;
case partroom.RoomEventType.MEMBER_KICKED:
return '被踢出房间';
return S.current.party_room_kicked;
default:
return null;
}
@@ -433,7 +434,7 @@ class _MessageItem extends ConsumerWidget {
final diff = now.difference(date);
if (diff.inMinutes < 1) {
return '刚刚';
return S.current.party_room_just_now;
} else if (diff.inMinutes < 60) {
return '${diff.inMinutes} 分钟前';
} else if (diff.inHours < 24) {
@@ -465,4 +466,4 @@ class _MessageItem extends ConsumerWidget {
),
);
}
}
}

View File

@@ -1,6 +1,7 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/provider/party_room.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
/// 信号发送器组件
class PartyRoomSignalSender extends ConsumerWidget {
@@ -25,7 +26,7 @@ class PartyRoomSignalSender extends ConsumerWidget {
const Spacer(),
DropDownButton(
leading: const Icon(FluentIcons.send, size: 16),
title: Text(signalTypes.isEmpty ? '加载中...' : '发送信号'),
title: Text(signalTypes.isEmpty ? S.current.party_room_loading : '发送信号'),
disabled: signalTypes.isEmpty || room == null,
items: signalTypes.map((signal) {
return MenuFlyoutItem(
@@ -52,7 +53,7 @@ class PartyRoomSignalSender extends ConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('发送失败'),
title: Text(S.current.party_room_send_failed),
content: Text(e.toString()),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
@@ -60,4 +61,4 @@ class PartyRoomSignalSender extends ConsumerWidget {
}
}
}
}
}

View File

@@ -1,5 +1,6 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
import 'package:starcitizen_doctor/ui/party_room/party_room_ui_model.dart';
/// 连接服务器页面
@@ -40,14 +41,14 @@ class PartyRoomConnectPage extends HookConsumerWidget {
const SizedBox(height: 32),
// 标题
const Text(
'组队大厅',
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0), letterSpacing: 2),
Text(
S.current.party_room_title,
style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0), letterSpacing: 2),
),
const SizedBox(height: 12),
// 副标题
Text('正在连接服务器...', style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: 0.7))),
Text(S.current.party_room_connecting, style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: 0.7))),
const SizedBox(height: 32),
// 加载动画
@@ -66,13 +67,13 @@ class PartyRoomConnectPage extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
Row(
children: [
Icon(FluentIcons.error_badge, color: Color(0xFFFF6B6B), size: 16),
SizedBox(width: 8),
const Icon(FluentIcons.error_badge, color: Color(0xFFFF6B6B), size: 16),
const SizedBox(width: 8),
Text(
'连接失败',
style: TextStyle(color: Color(0xFFFF6B6B), fontWeight: FontWeight.bold),
S.current.party_room_connect_failed,
style: const TextStyle(color: Color(0xFFFF6B6B), fontWeight: FontWeight.bold),
),
],
),
@@ -83,7 +84,7 @@ class PartyRoomConnectPage extends HookConsumerWidget {
onPressed: () async {
await uiModel.connectToServer();
},
child: const Text('重试'),
child: Text(S.current.party_room_retry),
),
],
),
@@ -95,4 +96,4 @@ class PartyRoomConnectPage extends HookConsumerWidget {
),
);
}
}
}

View File

@@ -66,7 +66,7 @@ class PartyRoomListPage extends HookConsumerWidget {
const SizedBox(width: 8),
Expanded(
child: Text(
'您正在以游客身份浏览,登录后可创建或加入房间。',
S.current.party_room_guest_mode_hint,
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.8)),
),
),
@@ -78,7 +78,7 @@ class PartyRoomListPage extends HookConsumerWidget {
onPressed: () {
ref.read(partyRoomUIModelProvider.notifier).exitGuestMode();
},
child: const Text('登录', style: TextStyle(fontSize: 12)),
child: Text(S.current.party_room_login, style: TextStyle(fontSize: 12)),
),
],
),
@@ -91,7 +91,7 @@ class PartyRoomListPage extends HookConsumerWidget {
Expanded(
child: TextBox(
controller: searchController,
placeholder: '搜索房主名称...',
placeholder: S.current.party_room_search_owner,
prefix: const Padding(padding: EdgeInsets.only(left: 8), child: Icon(FluentIcons.search)),
onSubmitted: (value) {
uiModel.loadRoomList(searchName: value, page: 1);
@@ -105,9 +105,9 @@ class PartyRoomListPage extends HookConsumerWidget {
const SizedBox(width: 12),
FilledButton(
onPressed: () => _showCreateRoomDialog(context, ref),
child: const Row(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [Icon(FluentIcons.add, size: 16), SizedBox(width: 8), Text('创建房间')],
children: [const Icon(FluentIcons.add, size: 16), const SizedBox(width: 8), Text(S.current.party_room_create_room)],
),
),
],
@@ -140,7 +140,7 @@ class PartyRoomListPage extends HookConsumerWidget {
padding: const EdgeInsets.all(16),
alignment: Alignment.bottomRight,
child: Tooltip(
message: '返回当前房间',
message: S.current.party_room_return_to_room,
child: GestureDetector(
onTap: () {
ref.read(partyRoomUIModelProvider.notifier).setMinimized(false);
@@ -177,10 +177,10 @@ class PartyRoomListPage extends HookConsumerWidget {
final tags = partyRoomState.room.tags;
return ComboBox<String>(
placeholder: const Text('选择标签'),
placeholder: Text(S.current.party_room_select_tag),
value: uiState.selectedMainTagId,
items: [
const ComboBoxItem(value: null, child: Text('全部标签')),
ComboBoxItem(value: null, child: Text(S.current.party_room_all_tags)),
...tags.values.map((tag) => ComboBoxItem(value: tag.id, child: Text(tag.name))),
],
onChanged: (value) {
@@ -213,7 +213,7 @@ class PartyRoomListPage extends HookConsumerWidget {
onPressed: () {
ref.read(partyRoomUIModelProvider.notifier).refreshRoomList();
},
child: const Text('重试'),
child: Text(S.current.party_room_retry),
),
],
),
@@ -229,17 +229,17 @@ class PartyRoomListPage extends HookConsumerWidget {
const SizedBox(height: 16),
Text(
uiState.searchOwnerName.isNotEmpty
? '没有找到符合条件的房间'
? S.current.party_room_no_matching_room
: uiState.selectedMainTagId != null
? '当前分类下没有房间'
: '暂无可用房间',
? S.current.party_room_no_room_in_category
: S.current.party_room_no_available_room,
style: TextStyle(color: Colors.white.withValues(alpha: 0.7)),
),
const SizedBox(height: 8),
if (uiState.searchOwnerName.isEmpty) ...[
Text('成为第一个创建房间的人吧!', style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.5))),
Text(S.current.party_room_be_first_create, style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.5))),
const SizedBox(height: 16),
FilledButton(onPressed: () => _showCreateRoomDialog(context, ref), child: const Text('创建房间')),
FilledButton(onPressed: () => _showCreateRoomDialog(context, ref), child: Text(S.current.party_room_create_room)),
],
],
),
@@ -264,7 +264,7 @@ class PartyRoomListPage extends HookConsumerWidget {
child: Center(
child: uiState.isLoading
? const ProgressRing()
: Text('已加载全部房间', style: TextStyle(color: Colors.white.withValues(alpha: 0.5))),
: Text(S.current.party_room_all_loaded, style: TextStyle(color: Colors.white.withValues(alpha: 0.5))),
),
);
}
@@ -431,11 +431,11 @@ class PartyRoomListPage extends HookConsumerWidget {
final shouldLogin = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('需要登录'),
content: const Text('创建房间需要先登录账号,是否现在去登录?'),
title: Text(S.current.party_room_need_login),
content: Text(S.current.party_room_create_need_login),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: const Text('去登录'), onPressed: () => Navigator.pop(context, true)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: Text(S.current.party_room_go_login), onPressed: () => Navigator.pop(context, true)),
],
),
);
@@ -451,11 +451,11 @@ class PartyRoomListPage extends HookConsumerWidget {
final shouldLeave = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('创建新房间'),
content: const Text('你已经在其他房间中,创建新房间将自动退出当前房间。是否继续?'),
title: Text(S.current.party_room_create_new_room),
content: Text(S.current.party_room_already_in_room_create),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: const Text('继续'), onPressed: () => Navigator.pop(context, true)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: Text(S.current.party_room_continue), onPressed: () => Navigator.pop(context, true)),
],
),
);
@@ -478,11 +478,11 @@ class PartyRoomListPage extends HookConsumerWidget {
final shouldLogin = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('需要登录'),
content: const Text('加入房间需要先登录账号,是否现在去登录?'),
title: Text(S.current.party_room_need_login),
content: Text(S.current.party_room_join_need_login),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: const Text('去登录'), onPressed: () => Navigator.pop(context, true)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: Text(S.current.party_room_go_login), onPressed: () => Navigator.pop(context, true)),
],
),
);
@@ -506,11 +506,11 @@ class PartyRoomListPage extends HookConsumerWidget {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => ContentDialog(
title: const Text('切换房间'),
content: const Text('你已经在其他房间中,加入新房间将自动退出当前房间。是否继续?'),
title: Text(S.current.party_room_switch_room),
content: Text(S.current.party_room_already_in_room_join),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: const Text('继续'), onPressed: () => Navigator.pop(context, true)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context, false)),
FilledButton(child: Text(S.current.party_room_continue), onPressed: () => Navigator.pop(context, true)),
],
),
);
@@ -533,14 +533,14 @@ class PartyRoomListPage extends HookConsumerWidget {
builder: (context) {
final passwordController = TextEditingController();
return ContentDialog(
title: const Text('输入房间密码'),
title: Text(S.current.party_room_enter_room_password),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [TextBox(controller: passwordController, placeholder: '请输入密码', obscureText: true)],
children: [TextBox(controller: passwordController, placeholder: S.current.party_room_enter_password_required, obscureText: true)],
),
actions: [
Button(child: const Text('取消'), onPressed: () => Navigator.pop(context)),
FilledButton(child: const Text('加入'), onPressed: () => Navigator.pop(context, passwordController.text)),
Button(child: Text(S.current.home_action_cancel), onPressed: () => Navigator.pop(context)),
FilledButton(child: Text(S.current.party_room_join), onPressed: () => Navigator.pop(context, passwordController.text)),
],
);
},
@@ -561,7 +561,7 @@ class PartyRoomListPage extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('加入失败'),
title: Text(S.current.party_room_join_failed),
content: Text(e.toString()),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
@@ -569,4 +569,4 @@ class PartyRoomListPage extends HookConsumerWidget {
}
}
}
}
}

View File

@@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:starcitizen_doctor/ui/party_room/party_room_ui_model.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
/// 注册页面
class PartyRoomRegisterPage extends HookConsumerWidget {
@@ -34,10 +35,10 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
},
icon: Padding(padding: const EdgeInsets.all(8.0), child: Icon(FluentIcons.back, size: 24)),
),
const Expanded(
Expanded(
child: Text(
'注册账号',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
S.current.party_room_register_title,
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
),
],
@@ -46,7 +47,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
if (uiState.errorMessage != null) ...[
InfoBar(
title: const Text('错误'),
title: Text(S.current.party_room_error),
content: Text(uiState.errorMessage!),
severity: InfoBarSeverity.error,
onClose: () => uiModel.clearError(),
@@ -60,7 +61,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
_buildStepIndicator(
context,
number: 1,
title: '输入游戏ID',
title: S.current.party_room_step_enter_game_id,
isActive: currentStep.value == 0,
isCompleted: currentStep.value > 0,
),
@@ -68,7 +69,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
_buildStepIndicator(
context,
number: 2,
title: '验证RSI账号',
title: S.current.party_room_step_verify_rsi,
isActive: currentStep.value == 1,
isCompleted: currentStep.value > 1,
),
@@ -76,7 +77,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
_buildStepIndicator(
context,
number: 3,
title: '完成注册',
title: S.current.party_room_step_complete,
isActive: currentStep.value == 2,
isCompleted: false,
),
@@ -95,8 +96,8 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
const SizedBox(height: 16),
InfoBar(
title: const Text('关于账号验证'),
content: const Text('接下来,您需要在 RSI 账号简介中添加验证码以证明账号所有权,验证通过后,您可以移除该验证码。'),
title: Text(S.current.party_room_about_verification),
content: Text(S.current.party_room_verification_hint),
severity: InfoBarSeverity.info,
),
],
@@ -159,21 +160,20 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
ValueNotifier<int> currentStep,
) {
return [
const Text(
'步骤 1: 输入您的游戏ID',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
Text(
S.current.party_room_step1_title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
const SizedBox(height: 12),
Text(
'请输入您在星际公民中的游戏IDHandle'
'这是您在游戏中使用的唯一标识符。',
S.current.party_room_step1_desc,
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.6)),
),
const SizedBox(height: 16),
TextBox(
controller: gameIdController,
placeholder: '例如: Citizen123',
placeholder: S.current.party_room_game_id_example,
enabled: !uiState.isLoading,
onSubmitted: (value) async {
if (value.trim().isEmpty) return;
@@ -189,7 +189,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
onPressed: () {
launchUrlString('https://robertsspaceindustries.com/en/account/dashboard');
},
child: const Text('查看我的游戏ID'),
child: Text(S.current.party_room_view_game_id),
),
const SizedBox(width: 8),
FilledButton(
@@ -201,8 +201,8 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
await showDialog(
context: context,
builder: (context) => ContentDialog(
title: const Text('提示'),
content: const Text('请输入游戏ID'),
title: Text(S.current.app_common_tip),
content: Text(S.current.party_room_enter_game_id),
actions: [FilledButton(child: const Text('确定'), onPressed: () => Navigator.pop(context))],
),
);
@@ -212,7 +212,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
},
child: uiState.isLoading
? const SizedBox(width: 16, height: 16, child: ProgressRing(strokeWidth: 2))
: const Text('下一步'),
: Text(S.current.party_room_next_step),
),
],
),
@@ -241,12 +241,12 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
ValueNotifier<int> currentStep,
) {
return [
const Text(
'步骤 2: 验证 RSI 账号',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
Text(
S.current.party_room_step2_title,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
const SizedBox(height: 12),
Text('请按照以下步骤完成账号验证:', style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.6))),
Text(S.current.party_room_step2_desc, style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.6))),
const SizedBox(height: 16),
Container(
@@ -259,9 +259,9 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'1. 复制以下验证码:',
style: TextStyle(fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
Text(
S.current.party_room_copy_code,
style: const TextStyle(fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
const SizedBox(height: 8),
Row(
@@ -285,25 +285,25 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
],
),
const SizedBox(height: 16),
const Text(
'2. 访问您的 RSI 账号资设置页',
style: TextStyle(fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
Text(
S.current.party_room_visit_rsi,
style: const TextStyle(fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
const SizedBox(height: 8),
Button(
onPressed: () {
launchUrlString('https://robertsspaceindustries.com/en/account/profile');
},
child: const Text('打开资料页'),
child: Text(S.current.party_room_open_profile),
),
const SizedBox(height: 16),
const Text(
'3. 编辑您的个人简介,将验证码添加到简介中',
style: TextStyle(fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
Text(
S.current.party_room_edit_bio,
style: const TextStyle(fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
const SizedBox(height: 8),
Text(
'在简介的任意位置添加验证码即可验证码30分钟内有效',
S.current.party_room_code_validity,
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: 0.5)),
),
],
@@ -318,7 +318,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
onPressed: () {
currentStep.value = 0;
},
child: const Text('上一步'),
child: Text(S.current.party_room_prev_step),
),
FilledButton(
onPressed: uiState.isLoading
@@ -328,7 +328,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
},
child: uiState.isLoading
? const SizedBox(width: 16, height: 16, child: ProgressRing(strokeWidth: 2))
: const Text('我已添加,验证并注册'),
: Text(S.current.party_room_verify_register),
),
],
),
@@ -356,15 +356,15 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
children: [
const Icon(FluentIcons.completed_solid, size: 64, color: Color(0xFF4CAF50)),
const SizedBox(height: 16),
const Text(
'注册成功!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
Text(
S.current.party_room_register_success,
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFFE0E0E0)),
),
const SizedBox(height: 8),
Text('您已成功注册组队大厅,现在可以开始使用了', style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.6))),
Text(S.current.party_room_register_success_msg, style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: 0.6))),
],
),
),
];
}
}
}

View File

@@ -100,15 +100,15 @@ class SplashUI extends HookConsumerWidget {
Text('诊断模式 - Step $currentStep', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
Row(
children: [
Button(onPressed: () => _loadDPrintLog(diagnosticLogs), child: const Text('读取完整日志')),
Button(onPressed: () => _loadDPrintLog(diagnosticLogs), child: Text(S.current.splash_read_full_log)),
const SizedBox(width: 8),
Button(onPressed: () => _resetHiveDatabase(context), child: const Text('重置数据库')),
Button(onPressed: () => _resetHiveDatabase(context), child: Text(S.current.splash_reset_database)),
],
),
],
),
const SizedBox(height: 16),
const Text('初始化任务执行情况:', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
Text(S.current.splash_init_task_status, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
const SizedBox(height: 12),
Expanded(
child: ValueListenableBuilder<List<String>>(
@@ -122,7 +122,7 @@ class SplashUI extends HookConsumerWidget {
border: Border.all(color: Colors.grey),
),
child: logs.isEmpty
? const Center(child: Text('等待日志...'))
? Center(child: Text(S.current.splash_waiting_log))
: ListView.builder(
itemCount: logs.length,
itemBuilder: (context, index) {
@@ -130,7 +130,7 @@ class SplashUI extends HookConsumerWidget {
Color textColor = Colors.white;
if (log.contains('')) {
textColor = Colors.green;
} else if (log.contains('') || log.contains('超时') || log.contains('错误')) {
} else if (log.contains('') || log.contains(S.current.splash_timeout) || log.contains(S.current.splash_error)) {
textColor = Colors.red;
} else if (log.contains('')) {
textColor = Colors.orange;
@@ -169,95 +169,95 @@ class SplashUI extends HookConsumerWidget {
addLog('[${DateTime.now().toIso8601String()}] 开始初始化...');
// Step 0: initApp with timeout
addLog('执行 appModel.initApp()...');
addLog(S.current.splash_exec_app_init);
try {
await appModel.initApp().timeout(
const Duration(seconds: 10),
onTimeout: () {
addLog('✗ appModel.initApp() 超时 (10秒)');
addLog(S.current.splash_app_init_timeout);
throw TimeoutException('initApp timeout');
},
);
addLog('✓ appModel.initApp() 完成');
addLog(S.current.splash_app_init_done);
} catch (e) {
addLog('✗ appModel.initApp() 错误: $e');
rethrow;
}
// Open app_conf box with timeout
addLog('打开 Hive app_conf box...');
addLog(S.current.splash_open_hive_box);
late Box appConf;
try {
appConf = await Hive.openBox("app_conf").timeout(
const Duration(seconds: 10),
onTimeout: () {
addLog('✗ Hive.openBox("app_conf") 超时 (10秒)');
addLog(S.current.splash_hive_timeout);
throw TimeoutException('openBox timeout');
},
);
addLog('✓ Hive.openBox("app_conf") 完成');
addLog(S.current.splash_hive_done);
} catch (e) {
addLog('✗ Hive.openBox("app_conf") 错误: $e');
rethrow;
}
// Check alert info version
addLog('检查 splash_alert_info_version...');
addLog(S.current.splash_check_version);
final v = appConf.get("splash_alert_info_version", defaultValue: 0);
addLog('✓ splash_alert_info_version = $v');
// Analytics touch
addLog('执行 AnalyticsApi.touch("launch")...');
addLog(S.current.splash_exec_analytics);
try {
final touchFuture = AnalyticsApi.touch("launch");
await touchFuture.timeout(
const Duration(seconds: 10),
onTimeout: () {
addLog('⚠ AnalyticsApi.touch() 超时 (10秒) - 继续执行');
addLog(S.current.splash_analytics_timeout);
},
);
addLog('✓ AnalyticsApi.touch("launch") 完成');
addLog(S.current.splash_analytics_done);
} catch (e) {
addLog('⚠ AnalyticsApi.touch("launch") 错误: $e - 继续执行');
}
// Show alert if needed
if (v < _alertInfoVersion) {
addLog('需要显示用户协议对话框...');
addLog(S.current.splash_show_agreement);
if (!context.mounted) {
addLog('✗ Context 已卸载,无法显示对话框');
addLog(S.current.splash_context_unmounted_dialog);
return;
}
await _showAlert(context, appConf);
addLog('✓ 用户协议对话框已处理');
addLog(S.current.splash_agreement_handled);
}
// Check host
addLog('执行 URLConf.checkHost()...');
addLog(S.current.splash_exec_check_host);
try {
final checkHostFuture = URLConf.checkHost();
await checkHostFuture.timeout(
const Duration(seconds: 10),
onTimeout: () {
addLog('⚠ URLConf.checkHost() 超时 (10秒) - 继续执行');
addLog(S.current.splash_check_host_timeout);
return false;
},
);
addLog('✓ URLConf.checkHost() 完成');
addLog(S.current.splash_check_host_done);
} catch (e) {
addLog('⚠ URLConf.checkHost() 错误: $e - 继续执行');
dPrint("checkHost Error:$e");
}
addLog('--- Step 0 完成,进入 Step 1 ---');
addLog(S.current.splash_step0_done);
stepState.value = 1;
if (!context.mounted) {
addLog('✗ Context 已卸载');
addLog(S.current.splash_context_unmounted);
return;
}
// Step 1: Check update
addLog('执行 appModel.checkUpdate()...');
addLog(S.current.splash_exec_check_update);
dPrint("_initApp checkUpdate");
try {
await appModel
@@ -265,37 +265,37 @@ class SplashUI extends HookConsumerWidget {
.timeout(
const Duration(seconds: 10),
onTimeout: () {
addLog('⚠ appModel.checkUpdate() 超时 (10秒) - 继续执行');
addLog(S.current.splash_check_update_timeout);
return false;
},
);
addLog('✓ appModel.checkUpdate() 完成');
addLog(S.current.splash_check_update_done);
} catch (e) {
addLog('⚠ appModel.checkUpdate() 错误: $e - 继续执行');
}
addLog('--- Step 1 完成,进入 Step 2 ---');
addLog(S.current.splash_step1_done);
stepState.value = 2;
// Step 2: Initialize aria2c
addLog('初始化 aria2cModelProvider...');
addLog(S.current.splash_init_aria2c);
dPrint("_initApp aria2cModelProvider");
try {
ref.read(aria2cModelProvider);
addLog('✓ aria2cModelProvider 初始化完成');
addLog(S.current.splash_aria2c_done);
} catch (e) {
addLog('⚠ aria2cModelProvider 初始化错误: $e');
}
if (!context.mounted) {
addLog('✗ Context 已卸载,无法导航');
addLog(S.current.splash_context_unmounted_nav);
return;
}
addLog('✓ 所有初始化完成,准备跳转到主界面');
addLog(S.current.splash_all_done);
await Future.delayed(const Duration(milliseconds: 500));
if (!context.mounted) {
addLog('✗ Context 已卸载,无法跳转');
addLog(S.current.splash_context_unmounted_jump);
return;
}
context.pushReplacement("/index");
@@ -348,12 +348,12 @@ class SplashUI extends HookConsumerWidget {
void _resetHiveDatabase(BuildContext context) async {
try {
dPrint('[诊断] 用户请求重置数据库');
dPrint(S.current.splash_user_reset_db);
// 关闭所有 Hive box
try {
await Hive.close();
dPrint('[诊断] Hive boxes 已关闭');
dPrint(S.current.splash_hive_boxes_closed);
} catch (e) {
dPrint('[诊断] 关闭 Hive boxes 失败: $e');
}
@@ -365,16 +365,16 @@ class SplashUI extends HookConsumerWidget {
if (await dbDir.exists()) {
dPrint('[诊断] 正在删除数据库目录: ${dbDir.path}');
await dbDir.delete(recursive: true);
dPrint('[诊断] 数据库目录已删除');
dPrint(S.current.splash_db_deleted);
} else {
dPrint('[诊断] 数据库目录不存在: ${dbDir.path}');
}
// 显示提示并退出
dPrint('[诊断] 数据库重置完成,准备退出应用');
dPrint(S.current.splash_db_reset_done);
if (context.mounted) {
await showToast(context, "数据库已重置,应用将退出。请重新启动应用。");
await showToast(context, S.current.splash_db_reset_msg);
}
// 等待一小段时间确保日志写入
@@ -382,7 +382,7 @@ class SplashUI extends HookConsumerWidget {
exit(0);
} catch (e) {
dPrint('[诊断] 重置数据库失败: $e');
dPrint(S.current.splash_reset_db_failed(e.toString()));
}
}
}
}