import 'dart:async'; import 'dart:convert'; import 'package:dart_rss/domain/rss_item.dart'; import 'package:fluent_ui/fluent_ui.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/api.dart'; import 'package:starcitizen_doctor/api/rss.dart'; import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart'; import 'package:starcitizen_doctor/common/utils/async.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:html/parser.dart' as html; import 'package:html/dom.dart' as html_dom; import 'package:url_launcher/url_launcher_string.dart'; import 'localization/localization_ui_model.dart'; 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 scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, @Default("") String lastScreenInfo, List? rssVideoItems, List? rssTextItems, MapEntry? localizationUpdateInfo, List? scServerStatus, List? countdownFestivalListData, @Default({}) Map isGameRunning, }) = _HomeUIModelState; } extension HomeUIModelStateEx on HomeUIModelState { bool get isCurGameRunning => isGameRunning[scInstalledPath] ?? false; } @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 reScanPath() async { state = state.copyWith(scInstalledPath: "not_install", lastScreenInfo: ""); } 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") { var image = node.attributes["src"]?.trim() ?? ""; var updatedImage = image.replaceAllMapped( RegExp(r'http(s)?://i(\d+)\.hdslb\.com/bfs/'), (match) => 'https://web-proxy.scbox.xkeyc.cn/bfs${match[2]}/bfs/' ); return updatedImage; } } } return ""; } String handleTitle(String? title) { if (title == null) return ""; title = title.replaceAll("【", "[ "); title = title.replaceAll("】", " ] "); return title; } // ignore: avoid_build_context_in_providers Future goWebView(BuildContext context, String title, String url, {bool useLocalization = false, bool loginMode = false}) async { launchUrlString(url); } 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 _updateSCServerStatus() async { try { final s = await Api.getScServerStatus(); dPrint("updateSCServerStatus===$s"); state = state.copyWith(scServerStatus: s); } catch (e) { dPrint(e); } } Future _loadRRS() async { try { final rssVideoItems = await RSSApi.getRssVideo(); state = state.copyWith(rssVideoItems: rssVideoItems); final rssTextItems = await RSSApi.getRssText(); state = state.copyWith(rssTextItems: rssTextItems); dPrint("RSS update Success !"); } catch (e) { dPrint("_loadRRS Error:$e"); } } Future checkLocalizationUpdate({bool skipReload = false}) async { dPrint("_checkLocalizationUpdate"); await (ref.read(localizationUIModelProvider.notifier)) .checkLangUpdate(skipReload: skipReload) .unwrap>(); } // ignore: avoid_build_context_in_providers launchRSI(BuildContext context) async { if (state.scInstalledPath == "not_install") { showToast(context, S.current.home_info_valid_installation_required); return; } } void onChangeInstallPath(String? value) { if (value == null) return; state = state.copyWith(scInstalledPath: value); } }