mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2024-12-23 05:23:44 +08:00
feat:riverpod 迁移 HomeUI
This commit is contained in:
parent
248b416277
commit
e70fca4899
@ -22,7 +22,6 @@ import 'api/api.dart';
|
|||||||
import 'common/helper/system_helper.dart';
|
import 'common/helper/system_helper.dart';
|
||||||
import 'common/io/rs_http.dart';
|
import 'common/io/rs_http.dart';
|
||||||
import 'common/rust/frb_generated.dart';
|
import 'common/rust/frb_generated.dart';
|
||||||
import 'common/utils/base_utils.dart';
|
|
||||||
import 'data/app_version_data.dart';
|
import 'data/app_version_data.dart';
|
||||||
import 'ui/index_ui.dart';
|
import 'ui/index_ui.dart';
|
||||||
import 'ui/settings/upgrade_dialog.dart';
|
import 'ui/settings/upgrade_dialog.dart';
|
||||||
|
738
lib/ui/home/home_ui.dart
Normal file
738
lib/ui/home/home_ui.dart
Normal file
@ -0,0 +1,738 @@
|
|||||||
|
import 'package:card_swiper/card_swiper.dart';
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
import 'home_ui_model.dart';
|
||||||
|
|
||||||
|
class HomeUI extends HookConsumerWidget {
|
||||||
|
const HomeUI({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final homeState = ref.watch(homeUIModelProvider);
|
||||||
|
final model = ref.watch(homeUIModelProvider.notifier);
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (homeState.appPlacardData != null) ...[
|
||||||
|
InfoBar(
|
||||||
|
title: Text("${homeState.appPlacardData?.title}"),
|
||||||
|
content: Text("${homeState.appPlacardData?.content}"),
|
||||||
|
severity: InfoBarSeverity.info,
|
||||||
|
action: homeState.appPlacardData?.link == null
|
||||||
|
? null
|
||||||
|
: Button(
|
||||||
|
child: const Text('查看详情'),
|
||||||
|
onPressed: () => _showPlacard(),
|
||||||
|
),
|
||||||
|
onClose: homeState.appPlacardData?.alwaysShow == true
|
||||||
|
? null
|
||||||
|
: () => model.closePlacard(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
],
|
||||||
|
...makeIndex(context, model, homeState)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (homeState.isFixing)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withAlpha(150),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const ProgressRing(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(homeState.isFixingString.isNotEmpty
|
||||||
|
? homeState.isFixingString
|
||||||
|
: "正在处理..."),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> makeIndex(
|
||||||
|
BuildContext context, HomeUIModel model, HomeUIModelState homeState) {
|
||||||
|
const double width = 280;
|
||||||
|
return [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 64, bottom: 0),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 30),
|
||||||
|
child: Image.asset(
|
||||||
|
"assets/sc_logo.png",
|
||||||
|
fit: BoxFit.fitHeight,
|
||||||
|
height: 260,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
makeGameStatusCard(context, model, 340, homeState)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 24,
|
||||||
|
child: makeLeftColumn(context, model, width, homeState),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 24,
|
||||||
|
top: 0,
|
||||||
|
child: makeNewsCard(context, model, homeState),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text("安装位置:"),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: ComboBox<String>(
|
||||||
|
value: homeState.scInstalledPath,
|
||||||
|
items: [
|
||||||
|
const ComboBoxItem(
|
||||||
|
value: "not_install",
|
||||||
|
child: Text("未安装 或 安装失败"),
|
||||||
|
),
|
||||||
|
for (final path in homeState.scInstallPaths)
|
||||||
|
ComboBoxItem(
|
||||||
|
value: path,
|
||||||
|
child: Text(path),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
onChanged: model.onChangeInstallPath,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Button(
|
||||||
|
onPressed: homeState.webLocalizationVersionsData == null
|
||||||
|
? null
|
||||||
|
: () => model.launchRSI(context),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(6),
|
||||||
|
child: Icon(
|
||||||
|
homeState.isCurGameRunning
|
||||||
|
? FluentIcons.stop_solid
|
||||||
|
: FluentIcons.play,
|
||||||
|
color: homeState.isCurGameRunning
|
||||||
|
? Colors.red.withOpacity(.8)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Button(
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(6),
|
||||||
|
child: Icon(FluentIcons.folder_open),
|
||||||
|
),
|
||||||
|
onPressed: () => SystemHelper.openDir(homeState.scInstalledPath),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Button(
|
||||||
|
onPressed: model.reScanPath,
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(6),
|
||||||
|
child: Icon(FluentIcons.refresh),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(homeState.lastScreenInfo, maxLines: 1),
|
||||||
|
makeIndexActionLists(context, model, homeState),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeLeftColumn(BuildContext context, HomeUIModel model, double width,
|
||||||
|
HomeUIModelState homeState) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: FluentTheme.of(context).cardColor.withOpacity(.03),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
makeWebViewButton(context, model,
|
||||||
|
icon: SvgPicture.asset(
|
||||||
|
"assets/rsi.svg",
|
||||||
|
colorFilter: makeSvgColor(Colors.white),
|
||||||
|
height: 18,
|
||||||
|
),
|
||||||
|
name: "星际公民官网汉化",
|
||||||
|
webTitle: "星际公民官网汉化",
|
||||||
|
webURL: "https://robertsspaceindustries.com",
|
||||||
|
info: "罗伯茨航天工业公司,万物的起源",
|
||||||
|
useLocalization: true,
|
||||||
|
width: width,
|
||||||
|
touchKey: "webLocalization_rsi"),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
makeWebViewButton(context, model,
|
||||||
|
icon: Row(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
"assets/uex.svg",
|
||||||
|
height: 18,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
name: "UEX 汉化",
|
||||||
|
webTitle: "UEX 汉化",
|
||||||
|
webURL: "https://uexcorp.space/",
|
||||||
|
info: "采矿、精炼、贸易计算器、价格、船信息",
|
||||||
|
useLocalization: true,
|
||||||
|
width: width,
|
||||||
|
touchKey: "webLocalization_uex"),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
makeWebViewButton(context, model,
|
||||||
|
icon: Row(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
"assets/dps.png",
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
name: "DPS计算器汉化",
|
||||||
|
webTitle: "DPS计算器汉化",
|
||||||
|
webURL: "https://www.erkul.games/live/calculator",
|
||||||
|
info: "在线改船,查询伤害数值和配件购买地点",
|
||||||
|
useLocalization: true,
|
||||||
|
width: width,
|
||||||
|
touchKey: "webLocalization_dps"),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
const Text("外部浏览器拓展:"),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Button(
|
||||||
|
child:
|
||||||
|
const FaIcon(FontAwesomeIcons.chrome, size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(
|
||||||
|
"https://chrome.google.com/webstore/detail/gocnjckojmledijgmadmacoikibcggja?authuser=0&hl=zh-CN");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Button(
|
||||||
|
child: const FaIcon(FontAwesomeIcons.edge, size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(
|
||||||
|
"https://microsoftedge.microsoft.com/addons/detail/lipbbcckldklpdcpfagicipecaacikgi");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Button(
|
||||||
|
child: const FaIcon(FontAwesomeIcons.firefoxBrowser,
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(
|
||||||
|
"https://addons.mozilla.org/zh-CN/firefox/"
|
||||||
|
"addon/%E6%98%9F%E9%99%85%E5%85%AC%E6%B0%91%E7%9B%92%E5%AD%90%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8B%93%E5%B1%95/");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Button(
|
||||||
|
child:
|
||||||
|
const FaIcon(FontAwesomeIcons.github, size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
launchUrlString(
|
||||||
|
"https://github.com/StarCitizenToolBox/StarCitizenBoxBrowserEx");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
makeActivityBanner(context, model, width, homeState),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (homeState.webLocalizationVersionsData == null)
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withOpacity(.3),
|
||||||
|
borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: const Center(
|
||||||
|
child: ProgressRing(),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeNewsCard(
|
||||||
|
BuildContext context, HomeUIModel model, HomeUIModelState homeState) {
|
||||||
|
return ScrollConfiguration(
|
||||||
|
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||||
|
child: Container(
|
||||||
|
width: 316,
|
||||||
|
height: 386,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(.1),
|
||||||
|
borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 190,
|
||||||
|
width: 316,
|
||||||
|
child: Tilt(
|
||||||
|
shadowConfig: const ShadowConfig(maxIntensity: .3),
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(12),
|
||||||
|
topRight: Radius.circular(12),
|
||||||
|
),
|
||||||
|
child: homeState.rssVideoItems == null
|
||||||
|
? Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(.1)),
|
||||||
|
child: makeLoading(context),
|
||||||
|
)
|
||||||
|
: Swiper(
|
||||||
|
itemCount: homeState.rssVideoItems?.length ?? 0,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = homeState.rssVideoItems![index];
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (item.link != null) {
|
||||||
|
launchUrlString(item.link!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: CacheNetImage(
|
||||||
|
url: model.getRssImage(item),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
autoplay: true,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const SizedBox(height: 1),
|
||||||
|
if (homeState.rssTextItems == null)
|
||||||
|
makeLoading(context)
|
||||||
|
else
|
||||||
|
ListView.builder(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final item = homeState.rssTextItems![index];
|
||||||
|
return Tilt(
|
||||||
|
shadowConfig: const ShadowConfig(maxIntensity: .3),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (item.link != null) {
|
||||||
|
launchUrlString(item.link!);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 12, right: 12, top: 4, bottom: 4),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
getRssIcon(item.link ?? ""),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
model.handleTitle(item.title),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(fontSize: 12.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white.withOpacity(.4),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
itemCount: homeState.rssTextItems?.length,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getRssIcon(String url) {
|
||||||
|
if (url.startsWith("https://tieba.baidu.com")) {
|
||||||
|
return SvgPicture.asset("assets/tieba.svg", width: 14, height: 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.startsWith("https://www.bilibili.com")) {
|
||||||
|
return const FaIcon(
|
||||||
|
FontAwesomeIcons.bilibili,
|
||||||
|
size: 14,
|
||||||
|
color: Color.fromRGBO(0, 161, 214, 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const FaIcon(FontAwesomeIcons.rss, size: 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeIndexActionLists(
|
||||||
|
BuildContext context, HomeUIModel model, HomeUIModelState homeState) {
|
||||||
|
final items = [
|
||||||
|
_HomeItemData("auto_check", "一键诊断", "一键诊断星际公民常见问题",
|
||||||
|
FluentIcons.auto_deploy_settings),
|
||||||
|
_HomeItemData(
|
||||||
|
"localization", "汉化管理", "快捷安装汉化资源", FluentIcons.locale_language),
|
||||||
|
_HomeItemData("performance", "性能优化", "调整引擎配置文件,优化游戏性能",
|
||||||
|
FluentIcons.process_meta_task),
|
||||||
|
];
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: AlignedGridView.count(
|
||||||
|
crossAxisCount: 3,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
itemCount: items.length,
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = items.elementAt(index);
|
||||||
|
return HoverButton(
|
||||||
|
onPressed: () => model.onMenuTap(item.key),
|
||||||
|
builder: (BuildContext context, Set<ButtonStates> states) {
|
||||||
|
return Container(
|
||||||
|
width: 300,
|
||||||
|
height: 120,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
color: states.isHovering
|
||||||
|
? FluentTheme.of(context).cardColor.withOpacity(.1)
|
||||||
|
: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(.2),
|
||||||
|
borderRadius: BorderRadius.circular(1000)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Icon(
|
||||||
|
item.icon,
|
||||||
|
size: 26,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 24),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.name,
|
||||||
|
style: const TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(item.infoString),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
if (item.key == "localization" &&
|
||||||
|
homeState.localizationUpdateInfo != null)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 3, bottom: 3, left: 8, right: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: Text(
|
||||||
|
homeState.localizationUpdateInfo?.key ?? " "),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 16,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeWebViewButton(BuildContext context, HomeUIModel model,
|
||||||
|
{required Widget icon,
|
||||||
|
required String name,
|
||||||
|
required String webTitle,
|
||||||
|
required String webURL,
|
||||||
|
required bool useLocalization,
|
||||||
|
required double width,
|
||||||
|
String? info,
|
||||||
|
String? touchKey}) {
|
||||||
|
return Tilt(
|
||||||
|
shadowConfig: const ShadowConfig(maxIntensity: .3),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (touchKey != null) {
|
||||||
|
AnalyticsApi.touch(touchKey);
|
||||||
|
}
|
||||||
|
model.goWebView(webTitle, webURL, useLocalization: true);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: const TextStyle(fontSize: 14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (info != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: Text(
|
||||||
|
info,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white.withOpacity(.6)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.white.withOpacity(.6),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeGameStatusCard(BuildContext context, HomeUIModel model,
|
||||||
|
double width, HomeUIModelState homeState) {
|
||||||
|
const statusCnName = {
|
||||||
|
"Platform": "平台",
|
||||||
|
"Persistent Universe": "持续宇宙",
|
||||||
|
"Electronic Access": "电子访问",
|
||||||
|
"Arena Commander": "竞技场指挥官"
|
||||||
|
};
|
||||||
|
|
||||||
|
return Tilt(
|
||||||
|
shadowConfig: const ShadowConfig(maxIntensity: .2),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
model.goWebView(
|
||||||
|
"RSI 服务器状态", "https://status.robertsspaceindustries.com/",
|
||||||
|
useLocalization: true);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: FluentTheme.of(context).cardColor,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Column(children: [
|
||||||
|
if (homeState.scServerStatus == null)
|
||||||
|
makeLoading(context, width: 20)
|
||||||
|
else
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const Text("状态:"),
|
||||||
|
for (final item in homeState.scServerStatus ?? [])
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 14,
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
FontAwesomeIcons.solidCircle,
|
||||||
|
color: model.isRSIServerStatusOK(item)
|
||||||
|
? Colors.green
|
||||||
|
: Colors.red,
|
||||||
|
size: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
"${statusCnName[item["name"]] ?? item["name"]}",
|
||||||
|
style: const TextStyle(fontSize: 13),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 12,
|
||||||
|
color: Colors.white.withOpacity(.4),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget makeActivityBanner(BuildContext context, HomeUIModel model,
|
||||||
|
double width, HomeUIModelState homeState) {
|
||||||
|
return Tilt(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
shadowConfig: const ShadowConfig(disable: true),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => _onTapFestival(),
|
||||||
|
child: Container(
|
||||||
|
width: width + 24,
|
||||||
|
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor),
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 12, right: 12, top: 8, bottom: 8),
|
||||||
|
child: (homeState.countdownFestivalListData == null)
|
||||||
|
? SizedBox(
|
||||||
|
width: width,
|
||||||
|
height: 62,
|
||||||
|
child: const Center(
|
||||||
|
child: ProgressRing(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: width,
|
||||||
|
height: 62,
|
||||||
|
child: Swiper(
|
||||||
|
itemCount: homeState.countdownFestivalListData!.length,
|
||||||
|
autoplay: true,
|
||||||
|
autoplayDelay: 5000,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item =
|
||||||
|
homeState.countdownFestivalListData![index];
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
if (item.icon != null && item.icon != "") ...[
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(1000),
|
||||||
|
child: Image.asset(
|
||||||
|
"assets/countdown/${item.icon}",
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.name ?? "",
|
||||||
|
style: const TextStyle(fontSize: 15),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
CountdownTimeText(
|
||||||
|
targetTime:
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
item.time ?? 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Icon(
|
||||||
|
FluentIcons.chevron_right,
|
||||||
|
size: 14,
|
||||||
|
color: Colors.white.withOpacity(.6),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_showPlacard() {}
|
||||||
|
|
||||||
|
_onTapFestival() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomeItemData {
|
||||||
|
String key;
|
||||||
|
|
||||||
|
_HomeItemData(this.key, this.name, this.infoString, this.icon);
|
||||||
|
|
||||||
|
String name;
|
||||||
|
String infoString;
|
||||||
|
IconData icon;
|
||||||
|
}
|
246
lib/ui/home/home_ui_model.dart
Normal file
246
lib/ui/home/home_ui_model.dart
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dart_rss/domain/rss_item.dart';
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:starcitizen_doctor/api/analytics.dart';
|
||||||
|
import 'package:starcitizen_doctor/api/api.dart';
|
||||||
|
import 'package:starcitizen_doctor/api/rss.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||||
|
import 'package:starcitizen_doctor/data/app_placard_data.dart';
|
||||||
|
import 'package:starcitizen_doctor/data/app_web_localization_versions_data.dart';
|
||||||
|
import 'package:starcitizen_doctor/data/countdown_festival_item_data.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
import 'package:html/parser.dart' as html;
|
||||||
|
import 'package:html/dom.dart' as html_dom;
|
||||||
|
|
||||||
|
part 'home_ui_model.freezed.dart';
|
||||||
|
|
||||||
|
part 'home_ui_model.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class HomeUIModelState with _$HomeUIModelState {
|
||||||
|
factory HomeUIModelState({
|
||||||
|
AppPlacardData? appPlacardData,
|
||||||
|
@Default(false) bool isFixing,
|
||||||
|
@Default("") String isFixingString,
|
||||||
|
String? scInstalledPath,
|
||||||
|
@Default([]) List<String> scInstallPaths,
|
||||||
|
AppWebLocalizationVersionsData? webLocalizationVersionsData,
|
||||||
|
@Default(false) bool isCurGameRunning,
|
||||||
|
@Default("") String lastScreenInfo,
|
||||||
|
List<RssItem>? rssVideoItems,
|
||||||
|
List<RssItem>? rssTextItems,
|
||||||
|
MapEntry<String, bool>? localizationUpdateInfo,
|
||||||
|
List? scServerStatus,
|
||||||
|
List<CountdownFestivalItemData>? countdownFestivalListData,
|
||||||
|
}) = _HomeUIModelState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class HomeUIModel extends _$HomeUIModel {
|
||||||
|
@override
|
||||||
|
HomeUIModelState build() {
|
||||||
|
state = HomeUIModelState();
|
||||||
|
_init();
|
||||||
|
_loadData();
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
closePlacard() async {
|
||||||
|
final box = await Hive.openBox("app_conf");
|
||||||
|
await box.put("close_placard", state.appPlacardData?.version);
|
||||||
|
state = state.copyWith(appPlacardData: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reScanPath() async {
|
||||||
|
state = state.copyWith(
|
||||||
|
scInstalledPath: "not_install", lastScreenInfo: "正在扫描 ...");
|
||||||
|
try {
|
||||||
|
final listData = await SCLoggerHelper.getLauncherLogList();
|
||||||
|
if (listData == null) {
|
||||||
|
state = state.copyWith(scInstalledPath: "not_install");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
|
||||||
|
withVersion: ["LIVE", "PTU", "EPTU"], checkExists: true);
|
||||||
|
String? scInstalledPath;
|
||||||
|
if (scInstallPaths.isNotEmpty) {
|
||||||
|
scInstalledPath = scInstallPaths.first;
|
||||||
|
}
|
||||||
|
final lastScreenInfo = "扫描完毕,共找到 ${scInstallPaths.length} 个有效安装目录";
|
||||||
|
state = state.copyWith(
|
||||||
|
scInstalledPath: scInstalledPath,
|
||||||
|
scInstallPaths: scInstallPaths,
|
||||||
|
lastScreenInfo: lastScreenInfo);
|
||||||
|
} catch (e) {
|
||||||
|
state = state.copyWith(
|
||||||
|
scInstalledPath: "not_install", lastScreenInfo: "解析 log 文件失败!");
|
||||||
|
AnalyticsApi.touch("error_launchLogs");
|
||||||
|
// showToast(context!,
|
||||||
|
// "解析 log 文件失败! \n请关闭游戏,退出RSI启动器后重试,若仍有问题,请使用工具箱中的 RSI Launcher log 修复。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getRssImage(RssItem item) {
|
||||||
|
final h = html.parse(item.description ?? "");
|
||||||
|
if (h.body == null) return "";
|
||||||
|
for (var node in h.body!.nodes) {
|
||||||
|
if (node is html_dom.Element) {
|
||||||
|
if (node.localName == "img") {
|
||||||
|
return node.attributes["src"]?.trim() ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String handleTitle(String? title) {
|
||||||
|
if (title == null) return "";
|
||||||
|
title = title.replaceAll("【", "[ ");
|
||||||
|
title = title.replaceAll("】", " ] ");
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMenuTap(String key) {}
|
||||||
|
|
||||||
|
void goWebView(String webTitle, String webURL,
|
||||||
|
{required bool useLocalization}) {}
|
||||||
|
|
||||||
|
bool isRSIServerStatusOK(Map map) {
|
||||||
|
return (map["status"] == "ok" || map["status"] == "operational");
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer? _serverUpdateTimer;
|
||||||
|
Timer? _appUpdateTimer;
|
||||||
|
|
||||||
|
void _init() {
|
||||||
|
reScanPath();
|
||||||
|
_serverUpdateTimer = Timer.periodic(
|
||||||
|
const Duration(minutes: 10),
|
||||||
|
(timer) {
|
||||||
|
_updateSCServerStatus();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
_appUpdateTimer = Timer.periodic(const Duration(minutes: 30), (timer) {
|
||||||
|
_checkLocalizationUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
ref.onDispose(() {
|
||||||
|
_serverUpdateTimer?.cancel();
|
||||||
|
_serverUpdateTimer = null;
|
||||||
|
_appUpdateTimer?.cancel();
|
||||||
|
_appUpdateTimer = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _loadData() async {
|
||||||
|
if (appGlobalState.networkVersionData == null) return;
|
||||||
|
try {
|
||||||
|
final r = await Api.getAppPlacard();
|
||||||
|
final box = await Hive.openBox("app_conf");
|
||||||
|
final version = box.get("close_placard", defaultValue: "");
|
||||||
|
if (r.enable == true) {
|
||||||
|
if (r.alwaysShow != true && version == r.version) {
|
||||||
|
} else {
|
||||||
|
state = state.copyWith(appPlacardData: r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final appWebLocalizationVersionsData =
|
||||||
|
AppWebLocalizationVersionsData.fromJson(json.decode(
|
||||||
|
(await RSHttp.getText(
|
||||||
|
"${URLConf.webTranslateHomeUrl}/versions.json"))));
|
||||||
|
final countdownFestivalListData = await Api.getFestivalCountdownList();
|
||||||
|
state = state.copyWith(
|
||||||
|
webLocalizationVersionsData: appWebLocalizationVersionsData,
|
||||||
|
countdownFestivalListData: countdownFestivalListData);
|
||||||
|
_updateSCServerStatus();
|
||||||
|
_loadRRS();
|
||||||
|
} catch (e) {
|
||||||
|
dPrint(e);
|
||||||
|
}
|
||||||
|
// check Localization update
|
||||||
|
_checkLocalizationUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateSCServerStatus() async {
|
||||||
|
try {
|
||||||
|
final s = await Api.getScServerStatus();
|
||||||
|
dPrint("updateSCServerStatus===$s");
|
||||||
|
state = state.copyWith(scServerStatus: s);
|
||||||
|
} catch (e) {
|
||||||
|
dPrint(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _loadRRS() async {
|
||||||
|
try {
|
||||||
|
state = state.copyWith(
|
||||||
|
rssVideoItems: await RSSApi.getRssVideo(),
|
||||||
|
rssTextItems: await RSSApi.getRssText());
|
||||||
|
dPrint("RSS update Success !");
|
||||||
|
} catch (e) {
|
||||||
|
dPrint("_loadRRS Error:$e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _checkLocalizationUpdate() {}
|
||||||
|
|
||||||
|
// ignore: avoid_build_context_in_providers
|
||||||
|
launchRSI(BuildContext context) async {
|
||||||
|
if (state.scInstalledPath == "not_install") {
|
||||||
|
showToast(context, "该功能需要一个有效的安装位置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConstConf.isMSE) {
|
||||||
|
if (state.isCurGameRunning) {
|
||||||
|
await Process.run(
|
||||||
|
SystemHelper.powershellPath, ["ps \"StarCitizen\" | kill"]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AnalyticsApi.touch("gameLaunch");
|
||||||
|
// showDialog(
|
||||||
|
// context: context,
|
||||||
|
// dismissWithEsc: false,
|
||||||
|
// builder: (context) {
|
||||||
|
// return BaseUIContainer(
|
||||||
|
// uiCreate: () => LoginDialog(),
|
||||||
|
// modelCreate: () => LoginDialogModel(scInstalledPath, this));
|
||||||
|
// });
|
||||||
|
} else {
|
||||||
|
final ok = await showConfirmDialogs(
|
||||||
|
context,
|
||||||
|
"一键启动功能提示",
|
||||||
|
const Text("为确保账户安全,一键启动功能已在开发版中禁用,我们将在微软商店版本中提供此功能。"
|
||||||
|
"\n\n微软商店版由微软提供可靠的分发下载与数字签名,可有效防止软件被恶意篡改。\n\n提示:您无需使用盒子启动游戏也可使用汉化。"),
|
||||||
|
confirm: "安装微软商店版本",
|
||||||
|
cancel: "取消");
|
||||||
|
if (ok == true) {
|
||||||
|
await launchUrlString(
|
||||||
|
"https://apps.microsoft.com/detail/9NF3SWFWNKL1?launch=true");
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChangeInstallPath(String? value) {
|
||||||
|
if (value == null) return;
|
||||||
|
state = state.copyWith(scInstalledPath: value);
|
||||||
|
}
|
||||||
|
}
|
458
lib/ui/home/home_ui_model.freezed.dart
Normal file
458
lib/ui/home/home_ui_model.freezed.dart
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'home_ui_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$HomeUIModelState {
|
||||||
|
AppPlacardData? get appPlacardData => throw _privateConstructorUsedError;
|
||||||
|
bool get isFixing => throw _privateConstructorUsedError;
|
||||||
|
String get isFixingString => throw _privateConstructorUsedError;
|
||||||
|
String? get scInstalledPath => throw _privateConstructorUsedError;
|
||||||
|
List<String> get scInstallPaths => throw _privateConstructorUsedError;
|
||||||
|
AppWebLocalizationVersionsData? get webLocalizationVersionsData =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
bool get isCurGameRunning => throw _privateConstructorUsedError;
|
||||||
|
String get lastScreenInfo => throw _privateConstructorUsedError;
|
||||||
|
List<RssItem>? get rssVideoItems => throw _privateConstructorUsedError;
|
||||||
|
List<RssItem>? get rssTextItems => throw _privateConstructorUsedError;
|
||||||
|
MapEntry<String, bool>? get localizationUpdateInfo =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
List<dynamic>? get scServerStatus => throw _privateConstructorUsedError;
|
||||||
|
List<CountdownFestivalItemData>? get countdownFestivalListData =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
$HomeUIModelStateCopyWith<HomeUIModelState> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $HomeUIModelStateCopyWith<$Res> {
|
||||||
|
factory $HomeUIModelStateCopyWith(
|
||||||
|
HomeUIModelState value, $Res Function(HomeUIModelState) then) =
|
||||||
|
_$HomeUIModelStateCopyWithImpl<$Res, HomeUIModelState>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{AppPlacardData? appPlacardData,
|
||||||
|
bool isFixing,
|
||||||
|
String isFixingString,
|
||||||
|
String? scInstalledPath,
|
||||||
|
List<String> scInstallPaths,
|
||||||
|
AppWebLocalizationVersionsData? webLocalizationVersionsData,
|
||||||
|
bool isCurGameRunning,
|
||||||
|
String lastScreenInfo,
|
||||||
|
List<RssItem>? rssVideoItems,
|
||||||
|
List<RssItem>? rssTextItems,
|
||||||
|
MapEntry<String, bool>? localizationUpdateInfo,
|
||||||
|
List<dynamic>? scServerStatus,
|
||||||
|
List<CountdownFestivalItemData>? countdownFestivalListData});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$HomeUIModelStateCopyWithImpl<$Res, $Val extends HomeUIModelState>
|
||||||
|
implements $HomeUIModelStateCopyWith<$Res> {
|
||||||
|
_$HomeUIModelStateCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? appPlacardData = freezed,
|
||||||
|
Object? isFixing = null,
|
||||||
|
Object? isFixingString = null,
|
||||||
|
Object? scInstalledPath = freezed,
|
||||||
|
Object? scInstallPaths = null,
|
||||||
|
Object? webLocalizationVersionsData = freezed,
|
||||||
|
Object? isCurGameRunning = null,
|
||||||
|
Object? lastScreenInfo = null,
|
||||||
|
Object? rssVideoItems = freezed,
|
||||||
|
Object? rssTextItems = freezed,
|
||||||
|
Object? localizationUpdateInfo = freezed,
|
||||||
|
Object? scServerStatus = freezed,
|
||||||
|
Object? countdownFestivalListData = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
appPlacardData: freezed == appPlacardData
|
||||||
|
? _value.appPlacardData
|
||||||
|
: appPlacardData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as AppPlacardData?,
|
||||||
|
isFixing: null == isFixing
|
||||||
|
? _value.isFixing
|
||||||
|
: isFixing // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
isFixingString: null == isFixingString
|
||||||
|
? _value.isFixingString
|
||||||
|
: isFixingString // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
scInstalledPath: freezed == scInstalledPath
|
||||||
|
? _value.scInstalledPath
|
||||||
|
: scInstalledPath // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
scInstallPaths: null == scInstallPaths
|
||||||
|
? _value.scInstallPaths
|
||||||
|
: scInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>,
|
||||||
|
webLocalizationVersionsData: freezed == webLocalizationVersionsData
|
||||||
|
? _value.webLocalizationVersionsData
|
||||||
|
: webLocalizationVersionsData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as AppWebLocalizationVersionsData?,
|
||||||
|
isCurGameRunning: null == isCurGameRunning
|
||||||
|
? _value.isCurGameRunning
|
||||||
|
: isCurGameRunning // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
lastScreenInfo: null == lastScreenInfo
|
||||||
|
? _value.lastScreenInfo
|
||||||
|
: lastScreenInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
rssVideoItems: freezed == rssVideoItems
|
||||||
|
? _value.rssVideoItems
|
||||||
|
: rssVideoItems // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<RssItem>?,
|
||||||
|
rssTextItems: freezed == rssTextItems
|
||||||
|
? _value.rssTextItems
|
||||||
|
: rssTextItems // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<RssItem>?,
|
||||||
|
localizationUpdateInfo: freezed == localizationUpdateInfo
|
||||||
|
? _value.localizationUpdateInfo
|
||||||
|
: localizationUpdateInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MapEntry<String, bool>?,
|
||||||
|
scServerStatus: freezed == scServerStatus
|
||||||
|
? _value.scServerStatus
|
||||||
|
: scServerStatus // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<dynamic>?,
|
||||||
|
countdownFestivalListData: freezed == countdownFestivalListData
|
||||||
|
? _value.countdownFestivalListData
|
||||||
|
: countdownFestivalListData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<CountdownFestivalItemData>?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$HomeUIModelStateImplCopyWith<$Res>
|
||||||
|
implements $HomeUIModelStateCopyWith<$Res> {
|
||||||
|
factory _$$HomeUIModelStateImplCopyWith(_$HomeUIModelStateImpl value,
|
||||||
|
$Res Function(_$HomeUIModelStateImpl) then) =
|
||||||
|
__$$HomeUIModelStateImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{AppPlacardData? appPlacardData,
|
||||||
|
bool isFixing,
|
||||||
|
String isFixingString,
|
||||||
|
String? scInstalledPath,
|
||||||
|
List<String> scInstallPaths,
|
||||||
|
AppWebLocalizationVersionsData? webLocalizationVersionsData,
|
||||||
|
bool isCurGameRunning,
|
||||||
|
String lastScreenInfo,
|
||||||
|
List<RssItem>? rssVideoItems,
|
||||||
|
List<RssItem>? rssTextItems,
|
||||||
|
MapEntry<String, bool>? localizationUpdateInfo,
|
||||||
|
List<dynamic>? scServerStatus,
|
||||||
|
List<CountdownFestivalItemData>? countdownFestivalListData});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$HomeUIModelStateImplCopyWithImpl<$Res>
|
||||||
|
extends _$HomeUIModelStateCopyWithImpl<$Res, _$HomeUIModelStateImpl>
|
||||||
|
implements _$$HomeUIModelStateImplCopyWith<$Res> {
|
||||||
|
__$$HomeUIModelStateImplCopyWithImpl(_$HomeUIModelStateImpl _value,
|
||||||
|
$Res Function(_$HomeUIModelStateImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? appPlacardData = freezed,
|
||||||
|
Object? isFixing = null,
|
||||||
|
Object? isFixingString = null,
|
||||||
|
Object? scInstalledPath = freezed,
|
||||||
|
Object? scInstallPaths = null,
|
||||||
|
Object? webLocalizationVersionsData = freezed,
|
||||||
|
Object? isCurGameRunning = null,
|
||||||
|
Object? lastScreenInfo = null,
|
||||||
|
Object? rssVideoItems = freezed,
|
||||||
|
Object? rssTextItems = freezed,
|
||||||
|
Object? localizationUpdateInfo = freezed,
|
||||||
|
Object? scServerStatus = freezed,
|
||||||
|
Object? countdownFestivalListData = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$HomeUIModelStateImpl(
|
||||||
|
appPlacardData: freezed == appPlacardData
|
||||||
|
? _value.appPlacardData
|
||||||
|
: appPlacardData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as AppPlacardData?,
|
||||||
|
isFixing: null == isFixing
|
||||||
|
? _value.isFixing
|
||||||
|
: isFixing // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
isFixingString: null == isFixingString
|
||||||
|
? _value.isFixingString
|
||||||
|
: isFixingString // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
scInstalledPath: freezed == scInstalledPath
|
||||||
|
? _value.scInstalledPath
|
||||||
|
: scInstalledPath // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
scInstallPaths: null == scInstallPaths
|
||||||
|
? _value._scInstallPaths
|
||||||
|
: scInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>,
|
||||||
|
webLocalizationVersionsData: freezed == webLocalizationVersionsData
|
||||||
|
? _value.webLocalizationVersionsData
|
||||||
|
: webLocalizationVersionsData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as AppWebLocalizationVersionsData?,
|
||||||
|
isCurGameRunning: null == isCurGameRunning
|
||||||
|
? _value.isCurGameRunning
|
||||||
|
: isCurGameRunning // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
lastScreenInfo: null == lastScreenInfo
|
||||||
|
? _value.lastScreenInfo
|
||||||
|
: lastScreenInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
rssVideoItems: freezed == rssVideoItems
|
||||||
|
? _value._rssVideoItems
|
||||||
|
: rssVideoItems // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<RssItem>?,
|
||||||
|
rssTextItems: freezed == rssTextItems
|
||||||
|
? _value._rssTextItems
|
||||||
|
: rssTextItems // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<RssItem>?,
|
||||||
|
localizationUpdateInfo: freezed == localizationUpdateInfo
|
||||||
|
? _value.localizationUpdateInfo
|
||||||
|
: localizationUpdateInfo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as MapEntry<String, bool>?,
|
||||||
|
scServerStatus: freezed == scServerStatus
|
||||||
|
? _value._scServerStatus
|
||||||
|
: scServerStatus // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<dynamic>?,
|
||||||
|
countdownFestivalListData: freezed == countdownFestivalListData
|
||||||
|
? _value._countdownFestivalListData
|
||||||
|
: countdownFestivalListData // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<CountdownFestivalItemData>?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$HomeUIModelStateImpl implements _HomeUIModelState {
|
||||||
|
_$HomeUIModelStateImpl(
|
||||||
|
{this.appPlacardData,
|
||||||
|
this.isFixing = false,
|
||||||
|
this.isFixingString = "",
|
||||||
|
this.scInstalledPath,
|
||||||
|
final List<String> scInstallPaths = const [],
|
||||||
|
this.webLocalizationVersionsData,
|
||||||
|
this.isCurGameRunning = false,
|
||||||
|
this.lastScreenInfo = "",
|
||||||
|
final List<RssItem>? rssVideoItems,
|
||||||
|
final List<RssItem>? rssTextItems,
|
||||||
|
this.localizationUpdateInfo,
|
||||||
|
final List<dynamic>? scServerStatus,
|
||||||
|
final List<CountdownFestivalItemData>? countdownFestivalListData})
|
||||||
|
: _scInstallPaths = scInstallPaths,
|
||||||
|
_rssVideoItems = rssVideoItems,
|
||||||
|
_rssTextItems = rssTextItems,
|
||||||
|
_scServerStatus = scServerStatus,
|
||||||
|
_countdownFestivalListData = countdownFestivalListData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final AppPlacardData? appPlacardData;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isFixing;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String isFixingString;
|
||||||
|
@override
|
||||||
|
final String? scInstalledPath;
|
||||||
|
final List<String> _scInstallPaths;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
List<String> get scInstallPaths {
|
||||||
|
if (_scInstallPaths is EqualUnmodifiableListView) return _scInstallPaths;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_scInstallPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final AppWebLocalizationVersionsData? webLocalizationVersionsData;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool isCurGameRunning;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String lastScreenInfo;
|
||||||
|
final List<RssItem>? _rssVideoItems;
|
||||||
|
@override
|
||||||
|
List<RssItem>? get rssVideoItems {
|
||||||
|
final value = _rssVideoItems;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_rssVideoItems is EqualUnmodifiableListView) return _rssVideoItems;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<RssItem>? _rssTextItems;
|
||||||
|
@override
|
||||||
|
List<RssItem>? get rssTextItems {
|
||||||
|
final value = _rssTextItems;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_rssTextItems is EqualUnmodifiableListView) return _rssTextItems;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final MapEntry<String, bool>? localizationUpdateInfo;
|
||||||
|
final List<dynamic>? _scServerStatus;
|
||||||
|
@override
|
||||||
|
List<dynamic>? get scServerStatus {
|
||||||
|
final value = _scServerStatus;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_scServerStatus is EqualUnmodifiableListView) return _scServerStatus;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<CountdownFestivalItemData>? _countdownFestivalListData;
|
||||||
|
@override
|
||||||
|
List<CountdownFestivalItemData>? get countdownFestivalListData {
|
||||||
|
final value = _countdownFestivalListData;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_countdownFestivalListData is EqualUnmodifiableListView)
|
||||||
|
return _countdownFestivalListData;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'HomeUIModelState(appPlacardData: $appPlacardData, isFixing: $isFixing, isFixingString: $isFixingString, scInstalledPath: $scInstalledPath, scInstallPaths: $scInstallPaths, webLocalizationVersionsData: $webLocalizationVersionsData, isCurGameRunning: $isCurGameRunning, lastScreenInfo: $lastScreenInfo, rssVideoItems: $rssVideoItems, rssTextItems: $rssTextItems, localizationUpdateInfo: $localizationUpdateInfo, scServerStatus: $scServerStatus, countdownFestivalListData: $countdownFestivalListData)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$HomeUIModelStateImpl &&
|
||||||
|
(identical(other.appPlacardData, appPlacardData) ||
|
||||||
|
other.appPlacardData == appPlacardData) &&
|
||||||
|
(identical(other.isFixing, isFixing) ||
|
||||||
|
other.isFixing == isFixing) &&
|
||||||
|
(identical(other.isFixingString, isFixingString) ||
|
||||||
|
other.isFixingString == isFixingString) &&
|
||||||
|
(identical(other.scInstalledPath, scInstalledPath) ||
|
||||||
|
other.scInstalledPath == scInstalledPath) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._scInstallPaths, _scInstallPaths) &&
|
||||||
|
(identical(other.webLocalizationVersionsData,
|
||||||
|
webLocalizationVersionsData) ||
|
||||||
|
other.webLocalizationVersionsData ==
|
||||||
|
webLocalizationVersionsData) &&
|
||||||
|
(identical(other.isCurGameRunning, isCurGameRunning) ||
|
||||||
|
other.isCurGameRunning == isCurGameRunning) &&
|
||||||
|
(identical(other.lastScreenInfo, lastScreenInfo) ||
|
||||||
|
other.lastScreenInfo == lastScreenInfo) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._rssVideoItems, _rssVideoItems) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._rssTextItems, _rssTextItems) &&
|
||||||
|
(identical(other.localizationUpdateInfo, localizationUpdateInfo) ||
|
||||||
|
other.localizationUpdateInfo == localizationUpdateInfo) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other._scServerStatus, _scServerStatus) &&
|
||||||
|
const DeepCollectionEquality().equals(
|
||||||
|
other._countdownFestivalListData, _countdownFestivalListData));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
appPlacardData,
|
||||||
|
isFixing,
|
||||||
|
isFixingString,
|
||||||
|
scInstalledPath,
|
||||||
|
const DeepCollectionEquality().hash(_scInstallPaths),
|
||||||
|
webLocalizationVersionsData,
|
||||||
|
isCurGameRunning,
|
||||||
|
lastScreenInfo,
|
||||||
|
const DeepCollectionEquality().hash(_rssVideoItems),
|
||||||
|
const DeepCollectionEquality().hash(_rssTextItems),
|
||||||
|
localizationUpdateInfo,
|
||||||
|
const DeepCollectionEquality().hash(_scServerStatus),
|
||||||
|
const DeepCollectionEquality().hash(_countdownFestivalListData));
|
||||||
|
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$HomeUIModelStateImplCopyWith<_$HomeUIModelStateImpl> get copyWith =>
|
||||||
|
__$$HomeUIModelStateImplCopyWithImpl<_$HomeUIModelStateImpl>(
|
||||||
|
this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _HomeUIModelState implements HomeUIModelState {
|
||||||
|
factory _HomeUIModelState(
|
||||||
|
{final AppPlacardData? appPlacardData,
|
||||||
|
final bool isFixing,
|
||||||
|
final String isFixingString,
|
||||||
|
final String? scInstalledPath,
|
||||||
|
final List<String> scInstallPaths,
|
||||||
|
final AppWebLocalizationVersionsData? webLocalizationVersionsData,
|
||||||
|
final bool isCurGameRunning,
|
||||||
|
final String lastScreenInfo,
|
||||||
|
final List<RssItem>? rssVideoItems,
|
||||||
|
final List<RssItem>? rssTextItems,
|
||||||
|
final MapEntry<String, bool>? localizationUpdateInfo,
|
||||||
|
final List<dynamic>? scServerStatus,
|
||||||
|
final List<CountdownFestivalItemData>? countdownFestivalListData}) =
|
||||||
|
_$HomeUIModelStateImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppPlacardData? get appPlacardData;
|
||||||
|
@override
|
||||||
|
bool get isFixing;
|
||||||
|
@override
|
||||||
|
String get isFixingString;
|
||||||
|
@override
|
||||||
|
String? get scInstalledPath;
|
||||||
|
@override
|
||||||
|
List<String> get scInstallPaths;
|
||||||
|
@override
|
||||||
|
AppWebLocalizationVersionsData? get webLocalizationVersionsData;
|
||||||
|
@override
|
||||||
|
bool get isCurGameRunning;
|
||||||
|
@override
|
||||||
|
String get lastScreenInfo;
|
||||||
|
@override
|
||||||
|
List<RssItem>? get rssVideoItems;
|
||||||
|
@override
|
||||||
|
List<RssItem>? get rssTextItems;
|
||||||
|
@override
|
||||||
|
MapEntry<String, bool>? get localizationUpdateInfo;
|
||||||
|
@override
|
||||||
|
List<dynamic>? get scServerStatus;
|
||||||
|
@override
|
||||||
|
List<CountdownFestivalItemData>? get countdownFestivalListData;
|
||||||
|
@override
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
_$$HomeUIModelStateImplCopyWith<_$HomeUIModelStateImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
25
lib/ui/home/home_ui_model.g.dart
Normal file
25
lib/ui/home/home_ui_model.g.dart
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'home_ui_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$homeUIModelHash() => r'7ab7b3721ff81a18d67717c9bc91632226c516f6';
|
||||||
|
|
||||||
|
/// See also [HomeUIModel].
|
||||||
|
@ProviderFor(HomeUIModel)
|
||||||
|
final homeUIModelProvider =
|
||||||
|
AutoDisposeNotifierProvider<HomeUIModel, HomeUIModelState>.internal(
|
||||||
|
HomeUIModel.new,
|
||||||
|
name: r'homeUIModelProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product') ? null : _$homeUIModelHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$HomeUIModel = AutoDisposeNotifier<HomeUIModelState>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
@ -3,6 +3,8 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/home/home_ui.dart';
|
||||||
|
import 'package:starcitizen_doctor/ui/home/home_ui_model.dart';
|
||||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@ -11,6 +13,9 @@ class IndexUI extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
// pre init child
|
||||||
|
ref.watch(homeUIModelProvider.select((value) => null));
|
||||||
|
|
||||||
final curIndex = useState(0);
|
final curIndex = useState(0);
|
||||||
return NavigationView(
|
return NavigationView(
|
||||||
appBar: NavigationAppBar(
|
appBar: NavigationAppBar(
|
||||||
@ -107,9 +112,14 @@ class IndexUI extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget getPage(int value) {
|
Widget getPage(int value) {
|
||||||
return Center(
|
switch (value) {
|
||||||
child: Text("$value"),
|
case 0:
|
||||||
);
|
return const HomeUI();
|
||||||
|
default:
|
||||||
|
return Center(
|
||||||
|
child: Text("UnimplPage $value"),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTapIndexMenu(String value, ValueNotifier<int> curIndexState) {
|
void _onTapIndexMenu(String value, ValueNotifier<int> curIndexState) {
|
||||||
|
@ -11,7 +11,6 @@ import 'package:starcitizen_doctor/app.dart';
|
|||||||
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
|
||||||
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
|
||||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||||
import 'package:html/parser.dart' as html_parser;
|
import 'package:html/parser.dart' as html_parser;
|
||||||
|
41
lib/widgets/src/cache_image.dart
Normal file
41
lib/widgets/src/cache_image.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import 'package:extended_image/extended_image.dart';
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
|
class CacheNetImage extends StatelessWidget {
|
||||||
|
final String url;
|
||||||
|
final double? width;
|
||||||
|
final double? height;
|
||||||
|
final BoxFit? fit;
|
||||||
|
|
||||||
|
const CacheNetImage(
|
||||||
|
{super.key, required this.url, this.width, this.height, this.fit});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ExtendedImage.network(
|
||||||
|
url,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
fit: fit,
|
||||||
|
loadStateChanged: (ExtendedImageState state) {
|
||||||
|
switch (state.extendedImageLoadState) {
|
||||||
|
case LoadState.loading:
|
||||||
|
return const Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ProgressRing(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
case LoadState.failed:
|
||||||
|
return const Text("Loading Image error");
|
||||||
|
case LoadState.completed:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
91
lib/widgets/src/countdown_time_text.dart
Normal file
91
lib/widgets/src/countdown_time_text.dart
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
|
class CountdownTimeText extends StatefulWidget {
|
||||||
|
final DateTime targetTime;
|
||||||
|
|
||||||
|
const CountdownTimeText({super.key, required this.targetTime});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CountdownTimeText> createState() => _CountdownTimeTextState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CountdownTimeTextState extends State<CountdownTimeText> {
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
Widget? textWidget;
|
||||||
|
|
||||||
|
bool stopTimer = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
initState() {
|
||||||
|
_onUpdateTime(null);
|
||||||
|
if (!stopTimer) {
|
||||||
|
_timer = Timer.periodic(const Duration(seconds: 1), _onUpdateTime);
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_timer = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onUpdateTime(_) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final dur = widget.targetTime.difference(now);
|
||||||
|
setState(() {
|
||||||
|
textWidget = _chineseTimeText(dur);
|
||||||
|
});
|
||||||
|
// 时间到,停止计时,并向宿主传递超时信息
|
||||||
|
if (dur.inMilliseconds <= 0) {
|
||||||
|
stopTimer = true;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
if (stopTimer) {
|
||||||
|
_timer?.cancel();
|
||||||
|
_timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _chineseTimeText(Duration duration) {
|
||||||
|
final surplus = duration;
|
||||||
|
int day = (surplus.inSeconds ~/ 3600) ~/ 24;
|
||||||
|
int hour = (surplus.inSeconds ~/ 3600) % 24;
|
||||||
|
int minute = surplus.inSeconds % 3600 ~/ 60;
|
||||||
|
int second = surplus.inSeconds % 60;
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"$day天 ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24, color: day < 30 ? Colors.red : Colors.white),
|
||||||
|
),
|
||||||
|
Text("${timePart(hour)}:${timePart(minute)}:${timePart(second)}"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String timePart(int p) {
|
||||||
|
if (p.toString().length == 1) return "0$p";
|
||||||
|
return "$p";
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (stopTimer) {
|
||||||
|
return const Text(
|
||||||
|
"正在进行中",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Color.fromRGBO(32, 220, 89, 1.0),
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return textWidget ?? const Text("");
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,25 @@ import 'package:window_manager/window_manager.dart';
|
|||||||
import 'package:markdown_widget/config/all.dart';
|
import 'package:markdown_widget/config/all.dart';
|
||||||
import 'package:markdown_widget/widget/all.dart';
|
import 'package:markdown_widget/widget/all.dart';
|
||||||
import 'package:extended_image/extended_image.dart';
|
import 'package:extended_image/extended_image.dart';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
export 'src/cache_image.dart';
|
||||||
|
export 'src/countdown_time_text.dart';
|
||||||
|
export '../common/utils/base_utils.dart';
|
||||||
|
|
||||||
|
Widget makeLoading(
|
||||||
|
BuildContext context, {
|
||||||
|
double? width,
|
||||||
|
}) {
|
||||||
|
width ??= 30;
|
||||||
|
return Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: width,
|
||||||
|
height: width,
|
||||||
|
child: const ProgressRing(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget makeDefaultPage(BuildContext context,
|
Widget makeDefaultPage(BuildContext context,
|
||||||
{Widget? titleRow,
|
{Widget? titleRow,
|
||||||
@ -99,6 +118,10 @@ List<Widget> makeMarkdownView(String description, {String? attachmentsUrl}) {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColorFilter makeSvgColor(Color color) {
|
||||||
|
return ui.ColorFilter.mode(color, ui.BlendMode.srcIn);
|
||||||
|
}
|
||||||
|
|
||||||
CustomTransitionPage<T> myPageBuilder<T>(
|
CustomTransitionPage<T> myPageBuilder<T>(
|
||||||
BuildContext context, GoRouterState state, Widget child) {
|
BuildContext context, GoRouterState state, Widget child) {
|
||||||
return CustomTransitionPage(
|
return CustomTransitionPage(
|
||||||
|
Loading…
Reference in New Issue
Block a user