feat: citizen news support

This commit is contained in:
xkeyC 2025-10-26 17:44:55 +08:00
parent 8d635827c4
commit c2a512699c
18 changed files with 1747 additions and 696 deletions

23
lib/api/news_api.dart Normal file
View File

@ -0,0 +1,23 @@
import 'dart:convert';
import '../common/conf/url_conf.dart';
import '../common/io/rs_http.dart';
import '../common/utils/log.dart';
import '../data/citizen_news_data.dart';
import 'api.dart';
class NewsApi {
static Future<CitizenNewsData?> getLatest() async {
try {
final data = await RSHttp.getText(
"${URLConf.newsApiHome}/api/latest",
withCustomDns: await Api.isUseInternalDNS(),
);
final map = json.decode(data);
return CitizenNewsData.fromJson(map);
} catch (e) {
dPrint("getLatestNews error: $e");
}
return null;
}
}

View File

@ -1,25 +0,0 @@
import 'dart:io';
import 'package:dart_rss/dart_rss.dart';
import 'package:starcitizen_doctor/common/io/rs_http.dart';
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
class RSSApi {
static Future<List<RssItem>> getRssVideo() async {
final r = await RSHttp.getText(URLConf.rssVideoUrl);
final f = RssFeed.parse(r);
return f.items.sublist(0, 8);
}
static Future<List<RssItem>> getRssText() async {
final r2 = await RSHttp.getText(URLConf.rssTextUrl2);
final r2f = RssFeed.parse(r2);
final items = r2f.items;
items.sort((a, b) {
final aDate = HttpDate.parse(a.pubDate ?? "").millisecondsSinceEpoch;
final bDate = HttpDate.parse(b.pubDate ?? "").millisecondsSinceEpoch;
return bDate - aDate;
});
return items;
}
}

View File

@ -7,7 +7,7 @@ import 'package:starcitizen_doctor/common/utils/log.dart';
class URLConf {
/// HOME API
static String gitApiHome = "https://git.scbox.xkeyc.cn";
static String rssApiHome = "https://rss.scbox.xkeyc.cn";
static String newsApiHome = "https://scbox.citizenwiki.cn";
static const String analyticsApiHome = "https://scbox.org";
static bool isUrlCheckPass = false;
@ -15,31 +15,22 @@ class URLConf {
/// URLS
static String get giteaAttachmentsUrl => "$gitApiHome/SCToolBox/Release";
static String get gitlabLocalizationUrl =>
"$gitApiHome/SCToolBox/LocalizationData";
static String get gitlabLocalizationUrl => "$gitApiHome/SCToolBox/LocalizationData";
static String get gitApiRSILauncherEnhanceUrl =>
"$gitApiHome/SCToolBox/RSILauncherEnhance";
static String get gitApiRSILauncherEnhanceUrl => "$gitApiHome/SCToolBox/RSILauncherEnhance";
static String get apiRepoPath => "$gitApiHome/SCToolBox/api/raw/branch/main";
static String get gitlabApiPath => "$gitApiHome/api/v1/";
static String get webTranslateHomeUrl =>
"$gitApiHome/SCToolBox/ScWeb_Chinese_Translate/raw/branch/main/json/locales";
static String get webTranslateHomeUrl => "$gitApiHome/SCToolBox/ScWeb_Chinese_Translate/raw/branch/main/json/locales";
static String get rssVideoUrl =>
"$rssApiHome/bilibili/user/channel/27976358/290653";
static String get rssTextUrl2 =>
"$rssApiHome/baidu/tieba/user/%E7%81%AC%E7%81%ACG%E7%81%AC%E7%81%AC&";
static const String googleTranslateApiUrl =
"https://translate-g-proxy.xkeyc.com";
static const String googleTranslateApiUrl = "https://translate-g-proxy.xkeyc.com";
static const feedbackUrl = "https://support.citizenwiki.cn/all";
static const feedbackFAQUrl = "https://support.citizenwiki.cn/t/sc-toolbox";
static String nav42KitUrl = "https://payload.citizenwiki.cn/api/community-navs?sort=is_sponsored&depth=2&page=1&limit=1000";
static String nav42KitUrl =
"https://payload.citizenwiki.cn/api/community-navs?sort=is_sponsored&depth=2&page=1&limit=1000";
static String get devReleaseUrl => "$gitApiHome/SCToolBox/Release/releases";
@ -47,19 +38,19 @@ class URLConf {
// 使 DNS
final gitApiList = _genFinalList(await dnsLookupTxt("git.dns.scbox.org"));
dPrint("DNS gitApiList ==== $gitApiList");
final fasterGit = await getFasterUrl(gitApiList);
final fasterGit = await getFasterUrl(gitApiList, "git");
dPrint("gitApiList.Faster ==== $fasterGit");
if (fasterGit != null) {
gitApiHome = fasterGit;
}
final rssApiList = _genFinalList(await dnsLookupTxt("rss.dns.scbox.org"));
final fasterRss = await getFasterUrl(rssApiList);
dPrint("DNS rssApiList ==== $rssApiList");
dPrint("rssApiList.Faster ==== $fasterRss");
if (fasterRss != null) {
rssApiHome = fasterRss;
final newsApiList = _genFinalList(await dnsLookupTxt("news.dns.scbox.org"));
final fasterNews = await getFasterUrl(newsApiList, "news");
dPrint("DNS newsApiList ==== $newsApiList");
dPrint("newsApiList.Faster ==== $fasterNews");
if (fasterNews != null) {
newsApiHome = fasterNews;
}
isUrlCheckPass = fasterGit != null && fasterRss != null;
isUrlCheckPass = fasterGit != null && fasterNews != null;
return isUrlCheckPass;
}
@ -72,7 +63,7 @@ class URLConf {
return (await DohClient.resolveTXT(host)) ?? [];
}
static Future<String?> getFasterUrl(List<String> urls) async {
static Future<String?> getFasterUrl(List<String> urls, String mode) async {
String firstUrl = "";
int callLen = 0;
@ -83,11 +74,23 @@ class URLConf {
}
}
for (var value in urls) {
RSHttp.head(value).then((resp) => onCall(resp, value), onError: (err) {
callLen++;
dPrint("RSHttp.head error $err");
});
for (var url in urls) {
var reqUrl = url;
switch (mode) {
case "git":
reqUrl = "$url/SCToolBox/Api/raw/branch/main/sc_doctor/version.json";
break;
case "news":
reqUrl = "$url/api/latest";
break;
}
RSHttp.head(reqUrl).then(
(resp) => onCall(resp, url),
onError: (err) {
callLen++;
dPrint("RSHttp.head error $err");
},
);
}
while (true) {

View File

@ -0,0 +1,54 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'citizen_news_data.freezed.dart';
part 'citizen_news_data.g.dart';
@freezed
sealed class CitizenNewsData with _$CitizenNewsData {
const factory CitizenNewsData({
@Default(<CitizenNewsVideosItemData>[]) @JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos,
@Default(<CitizenNewsArticlesItemData>[]) @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles,
}) = _CitizenNewsData;
const CitizenNewsData._();
factory CitizenNewsData.fromJson(Map<String, Object?> json) => _$CitizenNewsDataFromJson(json);
}
@freezed
sealed class CitizenNewsVideosItemData with _$CitizenNewsVideosItemData {
const factory CitizenNewsVideosItemData({
@Default('') @JsonKey(name: 'title') String title,
@Default('') @JsonKey(name: 'author') String author,
@Default('') @JsonKey(name: 'description') String description,
@Default('') @JsonKey(name: 'link') String link,
@Default('') @JsonKey(name: 'pubDate') String pubDate,
@Default('') @JsonKey(name: 'postId') String postId,
@Default(<String>[]) @JsonKey(name: 'detailedDescription') List<String> detailedDescription,
@Default('') @JsonKey(name: 'tag') String tag,
}) = _CitizenNewsVideosItemData;
const CitizenNewsVideosItemData._();
factory CitizenNewsVideosItemData.fromJson(Map<String, Object?> json) => _$CitizenNewsVideosItemDataFromJson(json);
}
@freezed
sealed class CitizenNewsArticlesItemData with _$CitizenNewsArticlesItemData {
const factory CitizenNewsArticlesItemData({
@Default('') @JsonKey(name: 'title') String title,
@Default('') @JsonKey(name: 'author') String author,
@Default('') @JsonKey(name: 'description') String description,
@Default('') @JsonKey(name: 'link') String link,
@Default('') @JsonKey(name: 'pubDate') String pubDate,
@Default('') @JsonKey(name: 'postId') String postId,
@Default(<String>[]) @JsonKey(name: 'detailedDescription') List<String> detailedDescription,
@Default('') @JsonKey(name: 'tag') String tag,
}) = _CitizenNewsArticlesItemData;
const CitizenNewsArticlesItemData._();
factory CitizenNewsArticlesItemData.fromJson(Map<String, Object?> json) =>
_$CitizenNewsArticlesItemDataFromJson(json);
}

View File

@ -0,0 +1,854 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// 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 'citizen_news_data.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$CitizenNewsData {
@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> get videos;@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> get articles;
/// Create a copy of CitizenNewsData
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CitizenNewsDataCopyWith<CitizenNewsData> get copyWith => _$CitizenNewsDataCopyWithImpl<CitizenNewsData>(this as CitizenNewsData, _$identity);
/// Serializes this CitizenNewsData to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CitizenNewsData&&const DeepCollectionEquality().equals(other.videos, videos)&&const DeepCollectionEquality().equals(other.articles, articles));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(videos),const DeepCollectionEquality().hash(articles));
@override
String toString() {
return 'CitizenNewsData(videos: $videos, articles: $articles)';
}
}
/// @nodoc
abstract mixin class $CitizenNewsDataCopyWith<$Res> {
factory $CitizenNewsDataCopyWith(CitizenNewsData value, $Res Function(CitizenNewsData) _then) = _$CitizenNewsDataCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos,@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles
});
}
/// @nodoc
class _$CitizenNewsDataCopyWithImpl<$Res>
implements $CitizenNewsDataCopyWith<$Res> {
_$CitizenNewsDataCopyWithImpl(this._self, this._then);
final CitizenNewsData _self;
final $Res Function(CitizenNewsData) _then;
/// Create a copy of CitizenNewsData
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? videos = null,Object? articles = null,}) {
return _then(_self.copyWith(
videos: null == videos ? _self.videos : videos // ignore: cast_nullable_to_non_nullable
as List<CitizenNewsVideosItemData>,articles: null == articles ? _self.articles : articles // ignore: cast_nullable_to_non_nullable
as List<CitizenNewsArticlesItemData>,
));
}
}
/// Adds pattern-matching-related methods to [CitizenNewsData].
extension CitizenNewsDataPatterns on CitizenNewsData {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CitizenNewsData value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CitizenNewsData() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CitizenNewsData value) $default,){
final _that = this;
switch (_that) {
case _CitizenNewsData():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CitizenNewsData value)? $default,){
final _that = this;
switch (_that) {
case _CitizenNewsData() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos, @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CitizenNewsData() when $default != null:
return $default(_that.videos,_that.articles);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos, @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles) $default,) {final _that = this;
switch (_that) {
case _CitizenNewsData():
return $default(_that.videos,_that.articles);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos, @JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles)? $default,) {final _that = this;
switch (_that) {
case _CitizenNewsData() when $default != null:
return $default(_that.videos,_that.articles);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _CitizenNewsData extends CitizenNewsData {
const _CitizenNewsData({@JsonKey(name: 'videos') final List<CitizenNewsVideosItemData> videos = const <CitizenNewsVideosItemData>[], @JsonKey(name: 'articles') final List<CitizenNewsArticlesItemData> articles = const <CitizenNewsArticlesItemData>[]}): _videos = videos,_articles = articles,super._();
factory _CitizenNewsData.fromJson(Map<String, dynamic> json) => _$CitizenNewsDataFromJson(json);
final List<CitizenNewsVideosItemData> _videos;
@override@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> get videos {
if (_videos is EqualUnmodifiableListView) return _videos;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_videos);
}
final List<CitizenNewsArticlesItemData> _articles;
@override@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> get articles {
if (_articles is EqualUnmodifiableListView) return _articles;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_articles);
}
/// Create a copy of CitizenNewsData
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CitizenNewsDataCopyWith<_CitizenNewsData> get copyWith => __$CitizenNewsDataCopyWithImpl<_CitizenNewsData>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$CitizenNewsDataToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CitizenNewsData&&const DeepCollectionEquality().equals(other._videos, _videos)&&const DeepCollectionEquality().equals(other._articles, _articles));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_videos),const DeepCollectionEquality().hash(_articles));
@override
String toString() {
return 'CitizenNewsData(videos: $videos, articles: $articles)';
}
}
/// @nodoc
abstract mixin class _$CitizenNewsDataCopyWith<$Res> implements $CitizenNewsDataCopyWith<$Res> {
factory _$CitizenNewsDataCopyWith(_CitizenNewsData value, $Res Function(_CitizenNewsData) _then) = __$CitizenNewsDataCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'videos') List<CitizenNewsVideosItemData> videos,@JsonKey(name: 'articles') List<CitizenNewsArticlesItemData> articles
});
}
/// @nodoc
class __$CitizenNewsDataCopyWithImpl<$Res>
implements _$CitizenNewsDataCopyWith<$Res> {
__$CitizenNewsDataCopyWithImpl(this._self, this._then);
final _CitizenNewsData _self;
final $Res Function(_CitizenNewsData) _then;
/// Create a copy of CitizenNewsData
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? videos = null,Object? articles = null,}) {
return _then(_CitizenNewsData(
videos: null == videos ? _self._videos : videos // ignore: cast_nullable_to_non_nullable
as List<CitizenNewsVideosItemData>,articles: null == articles ? _self._articles : articles // ignore: cast_nullable_to_non_nullable
as List<CitizenNewsArticlesItemData>,
));
}
}
/// @nodoc
mixin _$CitizenNewsVideosItemData {
@JsonKey(name: 'title') String get title;@JsonKey(name: 'author') String get author;@JsonKey(name: 'description') String get description;@JsonKey(name: 'link') String get link;@JsonKey(name: 'pubDate') String get pubDate;@JsonKey(name: 'postId') String get postId;@JsonKey(name: 'detailedDescription') List<String> get detailedDescription;@JsonKey(name: 'tag') String get tag;
/// Create a copy of CitizenNewsVideosItemData
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CitizenNewsVideosItemDataCopyWith<CitizenNewsVideosItemData> get copyWith => _$CitizenNewsVideosItemDataCopyWithImpl<CitizenNewsVideosItemData>(this as CitizenNewsVideosItemData, _$identity);
/// Serializes this CitizenNewsVideosItemData to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CitizenNewsVideosItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other.detailedDescription, detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(detailedDescription),tag);
@override
String toString() {
return 'CitizenNewsVideosItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
}
}
/// @nodoc
abstract mixin class $CitizenNewsVideosItemDataCopyWith<$Res> {
factory $CitizenNewsVideosItemDataCopyWith(CitizenNewsVideosItemData value, $Res Function(CitizenNewsVideosItemData) _then) = _$CitizenNewsVideosItemDataCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
});
}
/// @nodoc
class _$CitizenNewsVideosItemDataCopyWithImpl<$Res>
implements $CitizenNewsVideosItemDataCopyWith<$Res> {
_$CitizenNewsVideosItemDataCopyWithImpl(this._self, this._then);
final CitizenNewsVideosItemData _self;
final $Res Function(CitizenNewsVideosItemData) _then;
/// Create a copy of CitizenNewsVideosItemData
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
return _then(_self.copyWith(
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
as String,detailedDescription: null == detailedDescription ? _self.detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [CitizenNewsVideosItemData].
extension CitizenNewsVideosItemDataPatterns on CitizenNewsVideosItemData {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CitizenNewsVideosItemData value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CitizenNewsVideosItemData() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CitizenNewsVideosItemData value) $default,){
final _that = this;
switch (_that) {
case _CitizenNewsVideosItemData():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CitizenNewsVideosItemData value)? $default,){
final _that = this;
switch (_that) {
case _CitizenNewsVideosItemData() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CitizenNewsVideosItemData() when $default != null:
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag) $default,) {final _that = this;
switch (_that) {
case _CitizenNewsVideosItemData():
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,) {final _that = this;
switch (_that) {
case _CitizenNewsVideosItemData() when $default != null:
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _CitizenNewsVideosItemData extends CitizenNewsVideosItemData {
const _CitizenNewsVideosItemData({@JsonKey(name: 'title') this.title = '', @JsonKey(name: 'author') this.author = '', @JsonKey(name: 'description') this.description = '', @JsonKey(name: 'link') this.link = '', @JsonKey(name: 'pubDate') this.pubDate = '', @JsonKey(name: 'postId') this.postId = '', @JsonKey(name: 'detailedDescription') final List<String> detailedDescription = const <String>[], @JsonKey(name: 'tag') this.tag = ''}): _detailedDescription = detailedDescription,super._();
factory _CitizenNewsVideosItemData.fromJson(Map<String, dynamic> json) => _$CitizenNewsVideosItemDataFromJson(json);
@override@JsonKey(name: 'title') final String title;
@override@JsonKey(name: 'author') final String author;
@override@JsonKey(name: 'description') final String description;
@override@JsonKey(name: 'link') final String link;
@override@JsonKey(name: 'pubDate') final String pubDate;
@override@JsonKey(name: 'postId') final String postId;
final List<String> _detailedDescription;
@override@JsonKey(name: 'detailedDescription') List<String> get detailedDescription {
if (_detailedDescription is EqualUnmodifiableListView) return _detailedDescription;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_detailedDescription);
}
@override@JsonKey(name: 'tag') final String tag;
/// Create a copy of CitizenNewsVideosItemData
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CitizenNewsVideosItemDataCopyWith<_CitizenNewsVideosItemData> get copyWith => __$CitizenNewsVideosItemDataCopyWithImpl<_CitizenNewsVideosItemData>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$CitizenNewsVideosItemDataToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CitizenNewsVideosItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other._detailedDescription, _detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(_detailedDescription),tag);
@override
String toString() {
return 'CitizenNewsVideosItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
}
}
/// @nodoc
abstract mixin class _$CitizenNewsVideosItemDataCopyWith<$Res> implements $CitizenNewsVideosItemDataCopyWith<$Res> {
factory _$CitizenNewsVideosItemDataCopyWith(_CitizenNewsVideosItemData value, $Res Function(_CitizenNewsVideosItemData) _then) = __$CitizenNewsVideosItemDataCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
});
}
/// @nodoc
class __$CitizenNewsVideosItemDataCopyWithImpl<$Res>
implements _$CitizenNewsVideosItemDataCopyWith<$Res> {
__$CitizenNewsVideosItemDataCopyWithImpl(this._self, this._then);
final _CitizenNewsVideosItemData _self;
final $Res Function(_CitizenNewsVideosItemData) _then;
/// Create a copy of CitizenNewsVideosItemData
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
return _then(_CitizenNewsVideosItemData(
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
as String,detailedDescription: null == detailedDescription ? _self._detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
mixin _$CitizenNewsArticlesItemData {
@JsonKey(name: 'title') String get title;@JsonKey(name: 'author') String get author;@JsonKey(name: 'description') String get description;@JsonKey(name: 'link') String get link;@JsonKey(name: 'pubDate') String get pubDate;@JsonKey(name: 'postId') String get postId;@JsonKey(name: 'detailedDescription') List<String> get detailedDescription;@JsonKey(name: 'tag') String get tag;
/// Create a copy of CitizenNewsArticlesItemData
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$CitizenNewsArticlesItemDataCopyWith<CitizenNewsArticlesItemData> get copyWith => _$CitizenNewsArticlesItemDataCopyWithImpl<CitizenNewsArticlesItemData>(this as CitizenNewsArticlesItemData, _$identity);
/// Serializes this CitizenNewsArticlesItemData to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is CitizenNewsArticlesItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other.detailedDescription, detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(detailedDescription),tag);
@override
String toString() {
return 'CitizenNewsArticlesItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
}
}
/// @nodoc
abstract mixin class $CitizenNewsArticlesItemDataCopyWith<$Res> {
factory $CitizenNewsArticlesItemDataCopyWith(CitizenNewsArticlesItemData value, $Res Function(CitizenNewsArticlesItemData) _then) = _$CitizenNewsArticlesItemDataCopyWithImpl;
@useResult
$Res call({
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
});
}
/// @nodoc
class _$CitizenNewsArticlesItemDataCopyWithImpl<$Res>
implements $CitizenNewsArticlesItemDataCopyWith<$Res> {
_$CitizenNewsArticlesItemDataCopyWithImpl(this._self, this._then);
final CitizenNewsArticlesItemData _self;
final $Res Function(CitizenNewsArticlesItemData) _then;
/// Create a copy of CitizenNewsArticlesItemData
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
return _then(_self.copyWith(
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
as String,detailedDescription: null == detailedDescription ? _self.detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [CitizenNewsArticlesItemData].
extension CitizenNewsArticlesItemDataPatterns on CitizenNewsArticlesItemData {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CitizenNewsArticlesItemData value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CitizenNewsArticlesItemData() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CitizenNewsArticlesItemData value) $default,){
final _that = this;
switch (_that) {
case _CitizenNewsArticlesItemData():
return $default(_that);}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CitizenNewsArticlesItemData value)? $default,){
final _that = this;
switch (_that) {
case _CitizenNewsArticlesItemData() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CitizenNewsArticlesItemData() when $default != null:
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag) $default,) {final _that = this;
switch (_that) {
case _CitizenNewsArticlesItemData():
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'title') String title, @JsonKey(name: 'author') String author, @JsonKey(name: 'description') String description, @JsonKey(name: 'link') String link, @JsonKey(name: 'pubDate') String pubDate, @JsonKey(name: 'postId') String postId, @JsonKey(name: 'detailedDescription') List<String> detailedDescription, @JsonKey(name: 'tag') String tag)? $default,) {final _that = this;
switch (_that) {
case _CitizenNewsArticlesItemData() when $default != null:
return $default(_that.title,_that.author,_that.description,_that.link,_that.pubDate,_that.postId,_that.detailedDescription,_that.tag);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _CitizenNewsArticlesItemData extends CitizenNewsArticlesItemData {
const _CitizenNewsArticlesItemData({@JsonKey(name: 'title') this.title = '', @JsonKey(name: 'author') this.author = '', @JsonKey(name: 'description') this.description = '', @JsonKey(name: 'link') this.link = '', @JsonKey(name: 'pubDate') this.pubDate = '', @JsonKey(name: 'postId') this.postId = '', @JsonKey(name: 'detailedDescription') final List<String> detailedDescription = const <String>[], @JsonKey(name: 'tag') this.tag = ''}): _detailedDescription = detailedDescription,super._();
factory _CitizenNewsArticlesItemData.fromJson(Map<String, dynamic> json) => _$CitizenNewsArticlesItemDataFromJson(json);
@override@JsonKey(name: 'title') final String title;
@override@JsonKey(name: 'author') final String author;
@override@JsonKey(name: 'description') final String description;
@override@JsonKey(name: 'link') final String link;
@override@JsonKey(name: 'pubDate') final String pubDate;
@override@JsonKey(name: 'postId') final String postId;
final List<String> _detailedDescription;
@override@JsonKey(name: 'detailedDescription') List<String> get detailedDescription {
if (_detailedDescription is EqualUnmodifiableListView) return _detailedDescription;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_detailedDescription);
}
@override@JsonKey(name: 'tag') final String tag;
/// Create a copy of CitizenNewsArticlesItemData
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CitizenNewsArticlesItemDataCopyWith<_CitizenNewsArticlesItemData> get copyWith => __$CitizenNewsArticlesItemDataCopyWithImpl<_CitizenNewsArticlesItemData>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$CitizenNewsArticlesItemDataToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CitizenNewsArticlesItemData&&(identical(other.title, title) || other.title == title)&&(identical(other.author, author) || other.author == author)&&(identical(other.description, description) || other.description == description)&&(identical(other.link, link) || other.link == link)&&(identical(other.pubDate, pubDate) || other.pubDate == pubDate)&&(identical(other.postId, postId) || other.postId == postId)&&const DeepCollectionEquality().equals(other._detailedDescription, _detailedDescription)&&(identical(other.tag, tag) || other.tag == tag));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,title,author,description,link,pubDate,postId,const DeepCollectionEquality().hash(_detailedDescription),tag);
@override
String toString() {
return 'CitizenNewsArticlesItemData(title: $title, author: $author, description: $description, link: $link, pubDate: $pubDate, postId: $postId, detailedDescription: $detailedDescription, tag: $tag)';
}
}
/// @nodoc
abstract mixin class _$CitizenNewsArticlesItemDataCopyWith<$Res> implements $CitizenNewsArticlesItemDataCopyWith<$Res> {
factory _$CitizenNewsArticlesItemDataCopyWith(_CitizenNewsArticlesItemData value, $Res Function(_CitizenNewsArticlesItemData) _then) = __$CitizenNewsArticlesItemDataCopyWithImpl;
@override @useResult
$Res call({
@JsonKey(name: 'title') String title,@JsonKey(name: 'author') String author,@JsonKey(name: 'description') String description,@JsonKey(name: 'link') String link,@JsonKey(name: 'pubDate') String pubDate,@JsonKey(name: 'postId') String postId,@JsonKey(name: 'detailedDescription') List<String> detailedDescription,@JsonKey(name: 'tag') String tag
});
}
/// @nodoc
class __$CitizenNewsArticlesItemDataCopyWithImpl<$Res>
implements _$CitizenNewsArticlesItemDataCopyWith<$Res> {
__$CitizenNewsArticlesItemDataCopyWithImpl(this._self, this._then);
final _CitizenNewsArticlesItemData _self;
final $Res Function(_CitizenNewsArticlesItemData) _then;
/// Create a copy of CitizenNewsArticlesItemData
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? title = null,Object? author = null,Object? description = null,Object? link = null,Object? pubDate = null,Object? postId = null,Object? detailedDescription = null,Object? tag = null,}) {
return _then(_CitizenNewsArticlesItemData(
title: null == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String,author: null == author ? _self.author : author // ignore: cast_nullable_to_non_nullable
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
as String,link: null == link ? _self.link : link // ignore: cast_nullable_to_non_nullable
as String,pubDate: null == pubDate ? _self.pubDate : pubDate // ignore: cast_nullable_to_non_nullable
as String,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable
as String,detailedDescription: null == detailedDescription ? _self._detailedDescription : detailedDescription // ignore: cast_nullable_to_non_nullable
as List<String>,tag: null == tag ? _self.tag : tag // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@ -0,0 +1,91 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'citizen_news_data.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_CitizenNewsData _$CitizenNewsDataFromJson(
Map<String, dynamic> json,
) => _CitizenNewsData(
videos:
(json['videos'] as List<dynamic>?)
?.map(
(e) =>
CitizenNewsVideosItemData.fromJson(e as Map<String, dynamic>),
)
.toList() ??
const <CitizenNewsVideosItemData>[],
articles:
(json['articles'] as List<dynamic>?)
?.map(
(e) =>
CitizenNewsArticlesItemData.fromJson(e as Map<String, dynamic>),
)
.toList() ??
const <CitizenNewsArticlesItemData>[],
);
Map<String, dynamic> _$CitizenNewsDataToJson(_CitizenNewsData instance) =>
<String, dynamic>{'videos': instance.videos, 'articles': instance.articles};
_CitizenNewsVideosItemData _$CitizenNewsVideosItemDataFromJson(
Map<String, dynamic> json,
) => _CitizenNewsVideosItemData(
title: json['title'] as String? ?? '',
author: json['author'] as String? ?? '',
description: json['description'] as String? ?? '',
link: json['link'] as String? ?? '',
pubDate: json['pubDate'] as String? ?? '',
postId: json['postId'] as String? ?? '',
detailedDescription:
(json['detailedDescription'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const <String>[],
tag: json['tag'] as String? ?? '',
);
Map<String, dynamic> _$CitizenNewsVideosItemDataToJson(
_CitizenNewsVideosItemData instance,
) => <String, dynamic>{
'title': instance.title,
'author': instance.author,
'description': instance.description,
'link': instance.link,
'pubDate': instance.pubDate,
'postId': instance.postId,
'detailedDescription': instance.detailedDescription,
'tag': instance.tag,
};
_CitizenNewsArticlesItemData _$CitizenNewsArticlesItemDataFromJson(
Map<String, dynamic> json,
) => _CitizenNewsArticlesItemData(
title: json['title'] as String? ?? '',
author: json['author'] as String? ?? '',
description: json['description'] as String? ?? '',
link: json['link'] as String? ?? '',
pubDate: json['pubDate'] as String? ?? '',
postId: json['postId'] as String? ?? '',
detailedDescription:
(json['detailedDescription'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const <String>[],
tag: json['tag'] as String? ?? '',
);
Map<String, dynamic> _$CitizenNewsArticlesItemDataToJson(
_CitizenNewsArticlesItemData instance,
) => <String, dynamic>{
'title': instance.title,
'author': instance.author,
'description': instance.description,
'link': instance.link,
'pubDate': instance.pubDate,
'postId': instance.postId,
'detailedDescription': instance.detailedDescription,
'tag': instance.tag,
};

View File

@ -6,18 +6,17 @@ import 'package:starcitizen_doctor/widgets/widgets.dart';
class HomeMdContentDialogUI extends HookConsumerWidget {
final String title;
final String url;
final String? url;
final String? mdContent;
const HomeMdContentDialogUI(
{super.key, required this.title, required this.url});
const HomeMdContentDialogUI({super.key, required this.title, this.url, this.mdContent})
: assert(url != null || mdContent != null, "Either url or htmlContent must be provided");
@override
Widget build(BuildContext context, WidgetRef ref) {
return Material(
child: ContentDialog(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .6,
),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .6),
title: Text(title),
content: LoadingWidget(
onLoadData: _getContent,
@ -36,21 +35,24 @@ class HomeMdContentDialogUI extends HookConsumerWidget {
),
actions: [
FilledButton(
child: Padding(
padding:
const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
child: Text(S.current.action_close),
),
onPressed: () {
Navigator.pop(context);
})
child: Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
child: Text(S.current.action_close),
),
onPressed: () {
Navigator.pop(context);
},
),
],
),
);
}
Future<String> _getContent() async {
final r = await RSHttp.getText(url);
if (mdContent != null) {
return mdContent!;
}
final r = await RSHttp.getText(url!);
return r;
}
}

View File

@ -59,29 +59,27 @@ class HomeUI extends HookConsumerWidget {
),
const SizedBox(height: 6),
],
...makeIndex(context, model, homeState, ref)
...makeIndex(context, model, homeState, ref),
],
),
),
),
if (homeState.isFixing)
Container(
decoration: BoxDecoration(
color: Colors.black.withAlpha(150),
),
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
: S.current.doctor_info_processing),
Text(
homeState.isFixingString.isNotEmpty ? homeState.isFixingString : S.current.doctor_info_processing,
),
],
),
),
)
),
],
);
}
@ -100,28 +98,16 @@ class HomeUI extends HookConsumerWidget {
children: [
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Image.asset(
"assets/sc_logo.png",
fit: BoxFit.fitHeight,
height: 260,
),
child: Image.asset("assets/sc_logo.png", fit: BoxFit.fitHeight, height: 260),
),
makeGameStatusCard(context, model, 340, homeState)
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),
),
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),
@ -137,10 +123,7 @@ class HomeUI extends HookConsumerWidget {
await context.push("/guide");
await model.reScanPath();
},
child: const Padding(
padding: EdgeInsets.all(6),
child: Icon(FluentIcons.settings),
),
child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.settings)),
),
const SizedBox(width: 6),
Expanded(
@ -148,17 +131,12 @@ class HomeUI extends HookConsumerWidget {
value: homeState.scInstalledPath,
isExpanded: true,
items: [
ComboBoxItem(
value: "not_install",
child: Text(S.current.home_not_installed_or_failed),
),
ComboBoxItem(value: "not_install", child: Text(S.current.home_not_installed_or_failed)),
for (final path in homeState.scInstallPaths)
ComboBoxItem(
value: path,
child: Row(
children: [Text(path)],
),
)
child: Row(children: [Text(path)]),
),
],
onChanged: model.onChangeInstallPath,
),
@ -166,38 +144,29 @@ class HomeUI extends HookConsumerWidget {
if (S.current.app_language_code == NoL10n.langCodeZhCn) ...[
const SizedBox(width: 12),
Button(
onPressed: homeState.webLocalizationVersionsData == null ? null : () => model.launchRSI(context),
style: homeState.isCurGameRunning
? null
: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith(_getRunButtonColor),
),
child: Padding(
padding: const EdgeInsets.all(6),
child: Icon(
homeState.isCurGameRunning ? FluentIcons.stop_solid : FluentIcons.play_solid,
color: homeState.isCurGameRunning ? Colors.red.withValues(alpha: .8) : Colors.white,
),
)),
onPressed: homeState.webLocalizationVersionsData == null ? null : () => model.launchRSI(context),
style: homeState.isCurGameRunning
? null
: ButtonStyle(backgroundColor: WidgetStateProperty.resolveWith(_getRunButtonColor)),
child: Padding(
padding: const EdgeInsets.all(6),
child: Icon(
homeState.isCurGameRunning ? FluentIcons.stop_solid : FluentIcons.play_solid,
color: homeState.isCurGameRunning ? Colors.red.withValues(alpha: .8) : Colors.white,
),
),
),
],
const SizedBox(width: 12),
Button(
onPressed: () => _checkAndGoInputMethod(context, homeState, model, ref),
style: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith((_) => Colors.blue),
),
child: Padding(
padding: const EdgeInsets.all(6),
child: Icon(FluentIcons.keyboard_classic),
),
style: ButtonStyle(backgroundColor: WidgetStateProperty.resolveWith((_) => Colors.blue)),
child: Padding(padding: const EdgeInsets.all(6), child: Icon(FluentIcons.keyboard_classic)),
),
const SizedBox(width: 12),
Button(
onPressed: model.reScanPath,
child: const Padding(
padding: EdgeInsets.all(6),
child: Icon(FluentIcons.refresh),
),
child: const Padding(padding: EdgeInsets.all(6), child: Icon(FluentIcons.refresh)),
),
],
),
@ -223,55 +192,44 @@ class HomeUI extends HookConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
makeWebViewButton(context, model,
icon: SvgPicture.asset(
"assets/rsi.svg",
colorFilter: makeSvgColor(Colors.white),
height: 18,
),
name: S.current.home_action_star_citizen_website_localization,
webTitle: S.current.home_action_star_citizen_website_localization,
webURL: "https://robertsspaceindustries.com",
info: S.current.home_action_info_roberts_space_industries_origin,
useLocalization: true,
width: width,
touchKey: "webLocalization_rsi"),
makeWebViewButton(
context,
model,
icon: SvgPicture.asset("assets/rsi.svg", colorFilter: makeSvgColor(Colors.white), height: 18),
name: S.current.home_action_star_citizen_website_localization,
webTitle: S.current.home_action_star_citizen_website_localization,
webURL: "https://robertsspaceindustries.com",
info: S.current.home_action_info_roberts_space_industries_origin,
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: S.current.home_action_uex_localization,
webTitle: S.current.home_action_uex_localization,
webURL: "https://uexcorp.space/",
info: S.current.home_action_info_mining_refining_trade_calculator,
useLocalization: true,
width: width,
touchKey: "webLocalization_uex"),
makeWebViewButton(
context,
model,
icon: Row(children: [SvgPicture.asset("assets/uex.svg", height: 18), const SizedBox(width: 12)]),
name: S.current.home_action_uex_localization,
webTitle: S.current.home_action_uex_localization,
webURL: "https://uexcorp.space/",
info: S.current.home_action_info_mining_refining_trade_calculator,
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: S.current.home_action_dps_calculator_localization,
webTitle: S.current.home_action_dps_calculator_localization,
webURL: "https://www.erkul.games/live/calculator",
info: S.current.home_action_info_ship_upgrade_damage_value_query,
useLocalization: true,
width: width,
touchKey: "webLocalization_dps"),
makeWebViewButton(
context,
model,
icon: Row(children: [Image.asset("assets/dps.png", height: 20), const SizedBox(width: 12)]),
name: S.current.home_action_dps_calculator_localization,
webTitle: S.current.home_action_dps_calculator_localization,
webURL: "https://www.erkul.games/live/calculator",
info: S.current.home_action_info_ship_upgrade_damage_value_query,
useLocalization: true,
width: width,
touchKey: "webLocalization_dps",
),
const SizedBox(height: 12),
Text(S.current.home_action_external_browser_extension),
const SizedBox(height: 12),
@ -281,7 +239,8 @@ class HomeUI extends HookConsumerWidget {
child: const FaIcon(FontAwesomeIcons.chrome, size: 18),
onPressed: () {
launchUrlString(
"https://chrome.google.com/webstore/detail/gocnjckojmledijgmadmacoikibcggja?authuser=0&hl=zh-CN");
"https://chrome.google.com/webstore/detail/gocnjckojmledijgmadmacoikibcggja?authuser=0&hl=zh-CN",
);
},
),
const SizedBox(width: 12),
@ -289,15 +248,18 @@ class HomeUI extends HookConsumerWidget {
child: const FaIcon(FontAwesomeIcons.edge, size: 18),
onPressed: () {
launchUrlString(
"https://microsoftedge.microsoft.com/addons/detail/lipbbcckldklpdcpfagicipecaacikgi");
"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/");
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),
@ -308,7 +270,7 @@ class HomeUI extends HookConsumerWidget {
},
),
],
)
),
],
),
),
@ -319,13 +281,14 @@ class HomeUI extends HookConsumerWidget {
),
if (homeState.webLocalizationVersionsData == null)
Positioned.fill(
child: Container(
decoration:
BoxDecoration(color: Colors.black.withValues(alpha: .3), borderRadius: BorderRadius.circular(12)),
child: const Center(
child: ProgressRing(),
child: Container(
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: .3),
borderRadius: BorderRadius.circular(12),
),
child: const Center(child: ProgressRing()),
),
))
),
],
);
}
@ -334,95 +297,113 @@ class HomeUI extends HookConsumerWidget {
return ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: Container(
width: 316,
height: 386,
decoration: BoxDecoration(color: Colors.white.withValues(alpha: .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.withValues(alpha: .1)),
child: makeLoading(context),
)
: Swiper(
itemCount: getMinNumber([homeState.rssVideoItems?.length ?? 0, 6]),
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(
width: 316,
height: 386,
decoration: BoxDecoration(color: Colors.white.withValues(alpha: .1), borderRadius: BorderRadius.circular(12)),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 210,
width: 316,
child: homeState.citizenNewsData == null
? Container(
decoration: BoxDecoration(color: Colors.white.withValues(alpha: .1)),
child: makeLoading(context),
)
: HoverSwiper(
itemCount: getMinNumber([homeState.citizenNewsData?.videos.length ?? 0, 6]),
itemBuilder: (context, index) {
final item = homeState.citizenNewsData?.videos[index];
return GestureDetector(
onTap: () {
if (item.link != null) {
launchUrlString(item.link!);
}
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),
child: Column(
children: [
CacheNetImage(
url: model.getRssImage(item?.description),
fit: BoxFit.cover,
height: 180,
),
SizedBox(
height: 30,
child: Container(
width: double.infinity,
decoration: BoxDecoration(color: Colors.black),
padding: EdgeInsets.symmetric(horizontal: 6),
child: Center(
child: Row(
children: [
Expanded(
child: Text(
"${index + 1}. ${(item?.title ?? "").replaceAll("【寰宇周刊】", "")}",
style: TextStyle(fontSize: 12),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
const SizedBox(width: 12),
Icon(
FluentIcons.chevron_right,
size: 12,
color: Colors.white.withValues(alpha: .4),
)
],
),
),
],
),
));
},
itemCount: homeState.rssTextItems?.length,
),
const SizedBox(height: 12),
],
),
)),
);
},
paginationActiveSize: 8.0,
controlSize: 24,
controlPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
autoplayDelay: 5000,
),
),
const SizedBox(height: 1),
if (homeState.citizenNewsData?.articles == null)
makeLoading(context)
else
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
final item = homeState.citizenNewsData!.articles[index];
return Tilt(
shadowConfig: const ShadowConfig(maxIntensity: .3),
borderRadius: BorderRadius.circular(12),
child: GestureDetector(
onTap: () {
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.withValues(alpha: .4)),
],
),
),
),
);
},
itemCount: homeState.citizenNewsData?.articles.length ?? 0,
),
const SizedBox(height: 12),
],
),
),
),
);
}
@ -432,11 +413,7 @@ class HomeUI extends HookConsumerWidget {
}
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.bilibili, size: 14, color: Color.fromRGBO(0, 161, 214, 1));
}
return const FaIcon(FontAwesomeIcons.rss, size: 14);
@ -444,97 +421,104 @@ class HomeUI extends HookConsumerWidget {
Widget makeIndexActionLists(BuildContext context, HomeUIModel model, HomeUIModelState homeState, WidgetRef ref) {
final items = [
_HomeItemData("game_doctor", S.current.home_action_one_click_diagnosis,
S.current.home_action_info_one_click_diagnosis_star_citizen, FluentIcons.auto_deploy_settings),
_HomeItemData("localization", S.current.home_action_localization_management,
S.current.home_action_info_quick_install_localization_resources, FluentIcons.locale_language),
_HomeItemData("performance", S.current.home_action_performance_optimization,
S.current.home_action_info_engine_config_optimization, FluentIcons.process_meta_task),
_HomeItemData(
"game_doctor",
S.current.home_action_one_click_diagnosis,
S.current.home_action_info_one_click_diagnosis_star_citizen,
FluentIcons.auto_deploy_settings,
),
_HomeItemData(
"localization",
S.current.home_action_localization_management,
S.current.home_action_info_quick_install_localization_resources,
FluentIcons.locale_language,
),
_HomeItemData(
"performance",
S.current.home_action_performance_optimization,
S.current.home_action_info_engine_config_optimization,
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: () => _onMenuTap(context, item.key, homeState, ref),
builder: (BuildContext context, Set<WidgetState> states) {
return Container(
width: 300,
height: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: states.isHovered
? FluentTheme.of(context).cardColor.withValues(alpha: .1)
: FluentTheme.of(context).cardColor,
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: .2), borderRadius: BorderRadius.circular(1000)),
child: Padding(
padding: const EdgeInsets.all(12),
child: Icon(
item.icon,
size: 24,
),
),
crossAxisCount: 3,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
itemCount: items.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final item = items.elementAt(index);
return HoverButton(
onPressed: () => _onMenuTap(context, item.key, homeState, ref),
builder: (BuildContext context, Set<WidgetState> states) {
return Container(
width: 300,
height: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: states.isHovered
? FluentTheme.of(context).cardColor.withValues(alpha: .1)
: FluentTheme.of(context).cardColor,
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: .2),
borderRadius: BorderRadius.circular(1000),
),
const SizedBox(width: 24),
Expanded(
child: Column(
child: Padding(padding: const EdgeInsets.all(12), child: Icon(item.icon, size: 24)),
),
const SizedBox(width: 24),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.name,
style: const TextStyle(fontSize: 18),
),
Text(item.name, style: const TextStyle(fontSize: 18)),
const SizedBox(height: 4),
Text(
item.infoString,
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
),
],
)),
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,
)
],
),
),
),
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}) {
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),
@ -547,9 +531,7 @@ class HomeUI extends HookConsumerWidget {
},
child: Container(
width: width,
decoration: BoxDecoration(
color: FluentTheme.of(context).cardColor,
),
decoration: BoxDecoration(color: FluentTheme.of(context).cardColor),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
@ -561,10 +543,7 @@ class HomeUI extends HookConsumerWidget {
Row(
children: [
icon,
Text(
name,
style: const TextStyle(fontSize: 14),
),
Text(name, style: const TextStyle(fontSize: 14)),
],
),
if (info != null)
@ -575,16 +554,12 @@ class HomeUI extends HookConsumerWidget {
maxLines: 1,
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .6)),
),
)
),
],
),
),
const SizedBox(width: 12),
Icon(
FluentIcons.chevron_right,
size: 14,
color: Colors.white.withValues(alpha: .6),
)
Icon(FluentIcons.chevron_right, size: 14, color: Colors.white.withValues(alpha: .6)),
],
),
),
@ -598,7 +573,7 @@ class HomeUI extends HookConsumerWidget {
"Platform": S.current.home_action_rsi_status_platform,
"Persistent Universe": S.current.home_action_rsi_status_persistent_universe,
"Electronic Access": S.current.home_action_rsi_status_electronic_access,
"Arena Commander": S.current.home_action_rsi_status_arena_commander
"Arena Commander": S.current.home_action_rsi_status_arena_commander,
};
return Tilt(
@ -607,52 +582,48 @@ class HomeUI extends HookConsumerWidget {
child: GestureDetector(
onTap: () {
model.goWebView(
context, S.current.home_action_rsi_status_rsi_server_status, "https://status.robertsspaceindustries.com/",
useLocalization: true);
context,
S.current.home_action_rsi_status_rsi_server_status,
"https://status.robertsspaceindustries.com/",
useLocalization: true,
);
},
child: Container(
width: width,
decoration: BoxDecoration(
color: FluentTheme.of(context).cardColor,
),
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: [
Text(S.current.home_action_rsi_status_status),
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,
child: Column(
children: [
if (homeState.scServerStatus == null)
makeLoading(context, width: 20)
else
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(S.current.home_action_rsi_status_status),
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.withValues(alpha: .4),
)
],
)
]),
const SizedBox(width: 5),
Text("${statusCnName[item["name"]] ?? item["name"]}", style: const TextStyle(fontSize: 13)),
],
),
Icon(FluentIcons.chevron_right, size: 12, color: Colors.white.withValues(alpha: .4)),
],
),
],
),
),
),
),
@ -666,65 +637,55 @@ class HomeUI extends HookConsumerWidget {
child: GestureDetector(
onTap: () => _onTapFestival(context),
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: getMinNumber([homeState.countdownFestivalListData!.length, 6]),
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,
),
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: getMinNumber([homeState.countdownFestivalListData!.length, 6]),
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.withValues(alpha: .6),
)
],
);
},
),
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.withValues(alpha: .6)),
],
);
},
),
)),
),
),
),
),
);
}
@ -736,13 +697,14 @@ class HomeUI extends HookConsumerWidget {
return;
case "doc":
showDialog(
context: context,
builder: (context) {
return HomeMdContentDialogUI(
title: homeState.appPlacardData?.title ?? S.current.home_announcement_details,
url: homeState.appPlacardData?.link,
);
});
context: context,
builder: (context) {
return HomeMdContentDialogUI(
title: homeState.appPlacardData?.title ?? S.current.home_announcement_details,
url: homeState.appPlacardData?.link,
);
},
);
return;
}
}
@ -804,15 +766,22 @@ class HomeUI extends HookConsumerWidget {
}
void _checkAndGoInputMethod(
BuildContext context, HomeUIModelState homeState, HomeUIModel model, WidgetRef ref) async {
BuildContext context,
HomeUIModelState homeState,
HomeUIModel model,
WidgetRef ref,
) async {
final localizationState = ref.read(localizationUIModelProvider);
if (localizationState.communityInputMethodLanguageData == null) {
showToast(context, S.current.input_method_feature_maintenance);
return;
}
if (localizationState.installedCommunityInputMethodSupportVersion == null) {
final userOK = await showConfirmDialogs(context, S.current.input_method_community_input_method_not_installed,
Text(S.current.input_method_install_community_input_method_prompt));
final userOK = await showConfirmDialogs(
context,
S.current.input_method_community_input_method_not_installed,
Text(S.current.input_method_install_community_input_method_prompt),
);
if (userOK) {
if (!context.mounted) return;
() async {
@ -837,10 +806,7 @@ class HomeUI extends HookConsumerWidget {
}
Future<void> _goInputMethod(BuildContext context, HomeUIModel model) async {
await showDialog(
context: context,
builder: (context) => const InputMethodDialogUI(),
);
await showDialog(context: context, builder: (context) => const InputMethodDialogUI());
}
}

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dart_rss/domain/rss_item.dart';
import 'package:desktop_webview_window/desktop_webview_window.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -10,7 +9,7 @@ import 'package:hive_ce/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/api/news_api.dart';
import 'package:starcitizen_doctor/common/conf/conf.dart';
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
@ -23,12 +22,12 @@ 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/citizen_news_data.dart';
import 'package:starcitizen_doctor/data/countdown_festival_item_data.dart';
import 'package:starcitizen_doctor/ui/home/dialogs/home_game_login_dialog_ui.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:html/parser.dart' as html;
import 'package:html/dom.dart' as html_dom;
import '../webview/webview.dart';
import 'localization/localization_ui_model.dart';
@ -46,8 +45,7 @@ abstract class HomeUIModelState with _$HomeUIModelState {
@Default([]) List<String> scInstallPaths,
AppWebLocalizationVersionsData? webLocalizationVersionsData,
@Default("") String lastScreenInfo,
List<RssItem>? rssVideoItems,
List<RssItem>? rssTextItems,
CitizenNewsData? citizenNewsData,
MapEntry<String, bool>? localizationUpdateInfo,
List? scServerStatus,
List<CountdownFestivalItemData>? countdownFestivalListData,
@ -115,8 +113,9 @@ class HomeUIModel extends _$HomeUIModel {
}
}
String getRssImage(RssItem item) {
final h = html.parse(item.description ?? "");
String getRssImage(String? htmlString) {
if (htmlString == null) return "";
final h = html.parse(htmlString);
if (h.body == null) return "";
for (var node in h.body!.nodes) {
if (node is html_dom.Element) {
@ -135,14 +134,15 @@ class HomeUIModel extends _$HomeUIModel {
return title;
}
// ignore: avoid_build_context_in_providers
Future<void> goWebView(BuildContext context,
String title,
String url, {
bool useLocalization = false,
bool loginMode = false,
RsiLoginCallback? rsiLoginCallback,
}) async {
Future<void> goWebView(
// ignore: avoid_build_context_in_providers
BuildContext context,
String title,
String url, {
bool useLocalization = false,
bool loginMode = false,
RsiLoginCallback? rsiLoginCallback,
}) async {
if (useLocalization) {
const tipVersion = 2;
final box = await Hive.openBox("app_conf");
@ -153,10 +153,7 @@ class HomeUIModel extends _$HomeUIModel {
context,
S.current.home_action_title_star_citizen_website_localization,
Text(S.current.home_action_info_web_localization_plugin_disclaimer, style: const TextStyle(fontSize: 16)),
constraints: BoxConstraints(maxWidth: MediaQuery
.of(context)
.size
.width * .6),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .6),
);
if (!ok) {
if (loginMode) {
@ -227,7 +224,8 @@ class HomeUIModel extends _$HomeUIModel {
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 {
if (r.alwaysShow != true && version == r.version) {
} else {
state = state.copyWith(appPlacardData: r);
}
}
@ -241,7 +239,7 @@ class HomeUIModel extends _$HomeUIModel {
countdownFestivalListData: _fixFestivalCountdownListDateTime(countdownFestivalListData),
);
_updateSCServerStatus();
_loadRRS();
_loadNews();
} catch (e) {
dPrint(e);
}
@ -255,10 +253,10 @@ class HomeUIModel extends _$HomeUIModel {
return list.map((item) {
if (item.time == null) return item;
final itemDateTime = DateTime.fromMillisecondsSinceEpoch(item.time! * 1000);
final itemDateTime = DateTime.fromMillisecondsSinceEpoch(item.time!);
final itemDatePlusSeven = itemDateTime.add(const Duration(days: 7));
if (itemDatePlusSeven.isBefore(now)) {
final nextYearDate = DateTime(
final nextDate = DateTime(
now.year + 1,
itemDateTime.month,
itemDateTime.day,
@ -266,7 +264,7 @@ class HomeUIModel extends _$HomeUIModel {
itemDateTime.minute,
itemDateTime.second,
);
final newTimestamp = (nextYearDate.millisecondsSinceEpoch / 1000).round();
final newTimestamp = (nextDate.millisecondsSinceEpoch).round();
return CountdownFestivalItemData(name: item.name, time: newTimestamp, icon: item.icon);
}
@ -284,23 +282,9 @@ class HomeUIModel extends _$HomeUIModel {
}
}
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");
// loading
if (state.rssTextItems == null) {
state = state.copyWith(rssTextItems: []);
}
if (state.rssVideoItems == null) {
state = state.copyWith(rssVideoItems: []);
}
}
Future _loadNews() async {
final news = await NewsApi.getLatest();
state = state.copyWith(citizenNewsData: news ?? CitizenNewsData());
}
Future<void> checkLocalizationUpdate({bool skipReload = false}) async {
@ -362,12 +346,14 @@ class HomeUIModel extends _$HomeUIModel {
ref.read(localizationUIModelProvider.notifier).onChangeGameInstallPath(value);
}
Future<void> doLaunchGame(// ignore: avoid_build_context_in_providers
BuildContext context,
String launchExe,
List<String> args,
String installPath,
String? processorAffinity,) async {
Future<void> doLaunchGame(
// ignore: avoid_build_context_in_providers
BuildContext context,
String launchExe,
List<String> args,
String installPath,
String? processorAffinity,
) async {
var runningMap = Map<String, bool>.from(state.isGameRunning);
runningMap[installPath] = true;
state = state.copyWith(isGameRunning: runningMap);

View File

@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
/// @nodoc
mixin _$HomeUIModelState {
AppPlacardData? get appPlacardData; bool get isFixing; String get isFixingString; String? get scInstalledPath; List<String> get scInstallPaths; AppWebLocalizationVersionsData? get webLocalizationVersionsData; String get lastScreenInfo; List<RssItem>? get rssVideoItems; List<RssItem>? get rssTextItems; MapEntry<String, bool>? get localizationUpdateInfo; List? get scServerStatus; List<CountdownFestivalItemData>? get countdownFestivalListData; Map<String, bool> get isGameRunning;
AppPlacardData? get appPlacardData; bool get isFixing; String get isFixingString; String? get scInstalledPath; List<String> get scInstallPaths; AppWebLocalizationVersionsData? get webLocalizationVersionsData; String get lastScreenInfo; CitizenNewsData? get citizenNewsData; MapEntry<String, bool>? get localizationUpdateInfo; List? get scServerStatus; List<CountdownFestivalItemData>? get countdownFestivalListData; Map<String, bool> get isGameRunning;
/// Create a copy of HomeUIModelState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@ -25,16 +25,16 @@ $HomeUIModelStateCopyWith<HomeUIModelState> get copyWith => _$HomeUIModelStateCo
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeUIModelState&&(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.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)&&const DeepCollectionEquality().equals(other.isGameRunning, isGameRunning));
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomeUIModelState&&(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.lastScreenInfo, lastScreenInfo) || other.lastScreenInfo == lastScreenInfo)&&(identical(other.citizenNewsData, citizenNewsData) || other.citizenNewsData == citizenNewsData)&&(identical(other.localizationUpdateInfo, localizationUpdateInfo) || other.localizationUpdateInfo == localizationUpdateInfo)&&const DeepCollectionEquality().equals(other.scServerStatus, scServerStatus)&&const DeepCollectionEquality().equals(other.countdownFestivalListData, countdownFestivalListData)&&const DeepCollectionEquality().equals(other.isGameRunning, isGameRunning));
}
@override
int get hashCode => Object.hash(runtimeType,appPlacardData,isFixing,isFixingString,scInstalledPath,const DeepCollectionEquality().hash(scInstallPaths),webLocalizationVersionsData,lastScreenInfo,const DeepCollectionEquality().hash(rssVideoItems),const DeepCollectionEquality().hash(rssTextItems),localizationUpdateInfo,const DeepCollectionEquality().hash(scServerStatus),const DeepCollectionEquality().hash(countdownFestivalListData),const DeepCollectionEquality().hash(isGameRunning));
int get hashCode => Object.hash(runtimeType,appPlacardData,isFixing,isFixingString,scInstalledPath,const DeepCollectionEquality().hash(scInstallPaths),webLocalizationVersionsData,lastScreenInfo,citizenNewsData,localizationUpdateInfo,const DeepCollectionEquality().hash(scServerStatus),const DeepCollectionEquality().hash(countdownFestivalListData),const DeepCollectionEquality().hash(isGameRunning));
@override
String toString() {
return 'HomeUIModelState(appPlacardData: $appPlacardData, isFixing: $isFixing, isFixingString: $isFixingString, scInstalledPath: $scInstalledPath, scInstallPaths: $scInstallPaths, webLocalizationVersionsData: $webLocalizationVersionsData, lastScreenInfo: $lastScreenInfo, rssVideoItems: $rssVideoItems, rssTextItems: $rssTextItems, localizationUpdateInfo: $localizationUpdateInfo, scServerStatus: $scServerStatus, countdownFestivalListData: $countdownFestivalListData, isGameRunning: $isGameRunning)';
return 'HomeUIModelState(appPlacardData: $appPlacardData, isFixing: $isFixing, isFixingString: $isFixingString, scInstalledPath: $scInstalledPath, scInstallPaths: $scInstallPaths, webLocalizationVersionsData: $webLocalizationVersionsData, lastScreenInfo: $lastScreenInfo, citizenNewsData: $citizenNewsData, localizationUpdateInfo: $localizationUpdateInfo, scServerStatus: $scServerStatus, countdownFestivalListData: $countdownFestivalListData, isGameRunning: $isGameRunning)';
}
@ -45,11 +45,11 @@ abstract mixin class $HomeUIModelStateCopyWith<$Res> {
factory $HomeUIModelStateCopyWith(HomeUIModelState value, $Res Function(HomeUIModelState) _then) = _$HomeUIModelStateCopyWithImpl;
@useResult
$Res call({
AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, List<RssItem>? rssVideoItems, List<RssItem>? rssTextItems, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning
AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, CitizenNewsData? citizenNewsData, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning
});
$CitizenNewsDataCopyWith<$Res>? get citizenNewsData;
}
/// @nodoc
@ -62,7 +62,7 @@ class _$HomeUIModelStateCopyWithImpl<$Res>
/// Create a copy of HomeUIModelState
/// with the given fields replaced by the non-null parameter values.
@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? lastScreenInfo = null,Object? rssVideoItems = freezed,Object? rssTextItems = freezed,Object? localizationUpdateInfo = freezed,Object? scServerStatus = freezed,Object? countdownFestivalListData = freezed,Object? isGameRunning = null,}) {
@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? lastScreenInfo = null,Object? citizenNewsData = freezed,Object? localizationUpdateInfo = freezed,Object? scServerStatus = freezed,Object? countdownFestivalListData = freezed,Object? isGameRunning = null,}) {
return _then(_self.copyWith(
appPlacardData: freezed == appPlacardData ? _self.appPlacardData : appPlacardData // ignore: cast_nullable_to_non_nullable
as AppPlacardData?,isFixing: null == isFixing ? _self.isFixing : isFixing // ignore: cast_nullable_to_non_nullable
@ -71,16 +71,27 @@ as String,scInstalledPath: freezed == scInstalledPath ? _self.scInstalledPath :
as String?,scInstallPaths: null == scInstallPaths ? _self.scInstallPaths : scInstallPaths // ignore: cast_nullable_to_non_nullable
as List<String>,webLocalizationVersionsData: freezed == webLocalizationVersionsData ? _self.webLocalizationVersionsData : webLocalizationVersionsData // ignore: cast_nullable_to_non_nullable
as AppWebLocalizationVersionsData?,lastScreenInfo: null == lastScreenInfo ? _self.lastScreenInfo : lastScreenInfo // ignore: cast_nullable_to_non_nullable
as String,rssVideoItems: freezed == rssVideoItems ? _self.rssVideoItems : rssVideoItems // ignore: cast_nullable_to_non_nullable
as List<RssItem>?,rssTextItems: freezed == rssTextItems ? _self.rssTextItems : rssTextItems // ignore: cast_nullable_to_non_nullable
as List<RssItem>?,localizationUpdateInfo: freezed == localizationUpdateInfo ? _self.localizationUpdateInfo : localizationUpdateInfo // ignore: cast_nullable_to_non_nullable
as String,citizenNewsData: freezed == citizenNewsData ? _self.citizenNewsData : citizenNewsData // ignore: cast_nullable_to_non_nullable
as CitizenNewsData?,localizationUpdateInfo: freezed == localizationUpdateInfo ? _self.localizationUpdateInfo : localizationUpdateInfo // ignore: cast_nullable_to_non_nullable
as MapEntry<String, bool>?,scServerStatus: freezed == scServerStatus ? _self.scServerStatus : scServerStatus // ignore: cast_nullable_to_non_nullable
as List?,countdownFestivalListData: freezed == countdownFestivalListData ? _self.countdownFestivalListData : countdownFestivalListData // ignore: cast_nullable_to_non_nullable
as List<CountdownFestivalItemData>?,isGameRunning: null == isGameRunning ? _self.isGameRunning : isGameRunning // ignore: cast_nullable_to_non_nullable
as Map<String, bool>,
));
}
/// Create a copy of HomeUIModelState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$CitizenNewsDataCopyWith<$Res>? get citizenNewsData {
if (_self.citizenNewsData == null) {
return null;
}
return $CitizenNewsDataCopyWith<$Res>(_self.citizenNewsData!, (value) {
return _then(_self.copyWith(citizenNewsData: value));
});
}
}
@ -162,10 +173,10 @@ return $default(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, List<RssItem>? rssVideoItems, List<RssItem>? rssTextItems, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning)? $default,{required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, CitizenNewsData? citizenNewsData, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _HomeUIModelState() when $default != null:
return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.scInstalledPath,_that.scInstallPaths,_that.webLocalizationVersionsData,_that.lastScreenInfo,_that.rssVideoItems,_that.rssTextItems,_that.localizationUpdateInfo,_that.scServerStatus,_that.countdownFestivalListData,_that.isGameRunning);case _:
return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.scInstalledPath,_that.scInstallPaths,_that.webLocalizationVersionsData,_that.lastScreenInfo,_that.citizenNewsData,_that.localizationUpdateInfo,_that.scServerStatus,_that.countdownFestivalListData,_that.isGameRunning);case _:
return orElse();
}
@ -183,10 +194,10 @@ return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.s
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, List<RssItem>? rssVideoItems, List<RssItem>? rssTextItems, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning) $default,) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, CitizenNewsData? citizenNewsData, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning) $default,) {final _that = this;
switch (_that) {
case _HomeUIModelState():
return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.scInstalledPath,_that.scInstallPaths,_that.webLocalizationVersionsData,_that.lastScreenInfo,_that.rssVideoItems,_that.rssTextItems,_that.localizationUpdateInfo,_that.scServerStatus,_that.countdownFestivalListData,_that.isGameRunning);case _:
return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.scInstalledPath,_that.scInstallPaths,_that.webLocalizationVersionsData,_that.lastScreenInfo,_that.citizenNewsData,_that.localizationUpdateInfo,_that.scServerStatus,_that.countdownFestivalListData,_that.isGameRunning);case _:
throw StateError('Unexpected subclass');
}
@ -203,10 +214,10 @@ return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.s
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, List<RssItem>? rssVideoItems, List<RssItem>? rssTextItems, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning)? $default,) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, CitizenNewsData? citizenNewsData, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning)? $default,) {final _that = this;
switch (_that) {
case _HomeUIModelState() when $default != null:
return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.scInstalledPath,_that.scInstallPaths,_that.webLocalizationVersionsData,_that.lastScreenInfo,_that.rssVideoItems,_that.rssTextItems,_that.localizationUpdateInfo,_that.scServerStatus,_that.countdownFestivalListData,_that.isGameRunning);case _:
return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.scInstalledPath,_that.scInstallPaths,_that.webLocalizationVersionsData,_that.lastScreenInfo,_that.citizenNewsData,_that.localizationUpdateInfo,_that.scServerStatus,_that.countdownFestivalListData,_that.isGameRunning);case _:
return null;
}
@ -218,7 +229,7 @@ return $default(_that.appPlacardData,_that.isFixing,_that.isFixingString,_that.s
class _HomeUIModelState implements HomeUIModelState {
_HomeUIModelState({this.appPlacardData, this.isFixing = false, this.isFixingString = "", this.scInstalledPath, final List<String> scInstallPaths = const [], this.webLocalizationVersionsData, this.lastScreenInfo = "", final List<RssItem>? rssVideoItems, final List<RssItem>? rssTextItems, this.localizationUpdateInfo, final List? scServerStatus, final List<CountdownFestivalItemData>? countdownFestivalListData, final Map<String, bool> isGameRunning = const {}}): _scInstallPaths = scInstallPaths,_rssVideoItems = rssVideoItems,_rssTextItems = rssTextItems,_scServerStatus = scServerStatus,_countdownFestivalListData = countdownFestivalListData,_isGameRunning = isGameRunning;
_HomeUIModelState({this.appPlacardData, this.isFixing = false, this.isFixingString = "", this.scInstalledPath, final List<String> scInstallPaths = const [], this.webLocalizationVersionsData, this.lastScreenInfo = "", this.citizenNewsData, this.localizationUpdateInfo, final List? scServerStatus, final List<CountdownFestivalItemData>? countdownFestivalListData, final Map<String, bool> isGameRunning = const {}}): _scInstallPaths = scInstallPaths,_scServerStatus = scServerStatus,_countdownFestivalListData = countdownFestivalListData,_isGameRunning = isGameRunning;
@override final AppPlacardData? appPlacardData;
@ -234,24 +245,7 @@ class _HomeUIModelState implements HomeUIModelState {
@override final AppWebLocalizationVersionsData? webLocalizationVersionsData;
@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 CitizenNewsData? citizenNewsData;
@override final MapEntry<String, bool>? localizationUpdateInfo;
final List? _scServerStatus;
@override List? get scServerStatus {
@ -289,16 +283,16 @@ _$HomeUIModelStateCopyWith<_HomeUIModelState> get copyWith => __$HomeUIModelStat
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeUIModelState&&(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.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)&&const DeepCollectionEquality().equals(other._isGameRunning, _isGameRunning));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomeUIModelState&&(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.lastScreenInfo, lastScreenInfo) || other.lastScreenInfo == lastScreenInfo)&&(identical(other.citizenNewsData, citizenNewsData) || other.citizenNewsData == citizenNewsData)&&(identical(other.localizationUpdateInfo, localizationUpdateInfo) || other.localizationUpdateInfo == localizationUpdateInfo)&&const DeepCollectionEquality().equals(other._scServerStatus, _scServerStatus)&&const DeepCollectionEquality().equals(other._countdownFestivalListData, _countdownFestivalListData)&&const DeepCollectionEquality().equals(other._isGameRunning, _isGameRunning));
}
@override
int get hashCode => Object.hash(runtimeType,appPlacardData,isFixing,isFixingString,scInstalledPath,const DeepCollectionEquality().hash(_scInstallPaths),webLocalizationVersionsData,lastScreenInfo,const DeepCollectionEquality().hash(_rssVideoItems),const DeepCollectionEquality().hash(_rssTextItems),localizationUpdateInfo,const DeepCollectionEquality().hash(_scServerStatus),const DeepCollectionEquality().hash(_countdownFestivalListData),const DeepCollectionEquality().hash(_isGameRunning));
int get hashCode => Object.hash(runtimeType,appPlacardData,isFixing,isFixingString,scInstalledPath,const DeepCollectionEquality().hash(_scInstallPaths),webLocalizationVersionsData,lastScreenInfo,citizenNewsData,localizationUpdateInfo,const DeepCollectionEquality().hash(_scServerStatus),const DeepCollectionEquality().hash(_countdownFestivalListData),const DeepCollectionEquality().hash(_isGameRunning));
@override
String toString() {
return 'HomeUIModelState(appPlacardData: $appPlacardData, isFixing: $isFixing, isFixingString: $isFixingString, scInstalledPath: $scInstalledPath, scInstallPaths: $scInstallPaths, webLocalizationVersionsData: $webLocalizationVersionsData, lastScreenInfo: $lastScreenInfo, rssVideoItems: $rssVideoItems, rssTextItems: $rssTextItems, localizationUpdateInfo: $localizationUpdateInfo, scServerStatus: $scServerStatus, countdownFestivalListData: $countdownFestivalListData, isGameRunning: $isGameRunning)';
return 'HomeUIModelState(appPlacardData: $appPlacardData, isFixing: $isFixing, isFixingString: $isFixingString, scInstalledPath: $scInstalledPath, scInstallPaths: $scInstallPaths, webLocalizationVersionsData: $webLocalizationVersionsData, lastScreenInfo: $lastScreenInfo, citizenNewsData: $citizenNewsData, localizationUpdateInfo: $localizationUpdateInfo, scServerStatus: $scServerStatus, countdownFestivalListData: $countdownFestivalListData, isGameRunning: $isGameRunning)';
}
@ -309,11 +303,11 @@ abstract mixin class _$HomeUIModelStateCopyWith<$Res> implements $HomeUIModelSta
factory _$HomeUIModelStateCopyWith(_HomeUIModelState value, $Res Function(_HomeUIModelState) _then) = __$HomeUIModelStateCopyWithImpl;
@override @useResult
$Res call({
AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, List<RssItem>? rssVideoItems, List<RssItem>? rssTextItems, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning
AppPlacardData? appPlacardData, bool isFixing, String isFixingString, String? scInstalledPath, List<String> scInstallPaths, AppWebLocalizationVersionsData? webLocalizationVersionsData, String lastScreenInfo, CitizenNewsData? citizenNewsData, MapEntry<String, bool>? localizationUpdateInfo, List? scServerStatus, List<CountdownFestivalItemData>? countdownFestivalListData, Map<String, bool> isGameRunning
});
@override $CitizenNewsDataCopyWith<$Res>? get citizenNewsData;
}
/// @nodoc
@ -326,7 +320,7 @@ class __$HomeUIModelStateCopyWithImpl<$Res>
/// Create a copy of HomeUIModelState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? appPlacardData = freezed,Object? isFixing = null,Object? isFixingString = null,Object? scInstalledPath = freezed,Object? scInstallPaths = null,Object? webLocalizationVersionsData = freezed,Object? lastScreenInfo = null,Object? rssVideoItems = freezed,Object? rssTextItems = freezed,Object? localizationUpdateInfo = freezed,Object? scServerStatus = freezed,Object? countdownFestivalListData = freezed,Object? isGameRunning = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? appPlacardData = freezed,Object? isFixing = null,Object? isFixingString = null,Object? scInstalledPath = freezed,Object? scInstallPaths = null,Object? webLocalizationVersionsData = freezed,Object? lastScreenInfo = null,Object? citizenNewsData = freezed,Object? localizationUpdateInfo = freezed,Object? scServerStatus = freezed,Object? countdownFestivalListData = freezed,Object? isGameRunning = null,}) {
return _then(_HomeUIModelState(
appPlacardData: freezed == appPlacardData ? _self.appPlacardData : appPlacardData // ignore: cast_nullable_to_non_nullable
as AppPlacardData?,isFixing: null == isFixing ? _self.isFixing : isFixing // ignore: cast_nullable_to_non_nullable
@ -335,9 +329,8 @@ as String,scInstalledPath: freezed == scInstalledPath ? _self.scInstalledPath :
as String?,scInstallPaths: null == scInstallPaths ? _self._scInstallPaths : scInstallPaths // ignore: cast_nullable_to_non_nullable
as List<String>,webLocalizationVersionsData: freezed == webLocalizationVersionsData ? _self.webLocalizationVersionsData : webLocalizationVersionsData // ignore: cast_nullable_to_non_nullable
as AppWebLocalizationVersionsData?,lastScreenInfo: null == lastScreenInfo ? _self.lastScreenInfo : lastScreenInfo // ignore: cast_nullable_to_non_nullable
as String,rssVideoItems: freezed == rssVideoItems ? _self._rssVideoItems : rssVideoItems // ignore: cast_nullable_to_non_nullable
as List<RssItem>?,rssTextItems: freezed == rssTextItems ? _self._rssTextItems : rssTextItems // ignore: cast_nullable_to_non_nullable
as List<RssItem>?,localizationUpdateInfo: freezed == localizationUpdateInfo ? _self.localizationUpdateInfo : localizationUpdateInfo // ignore: cast_nullable_to_non_nullable
as String,citizenNewsData: freezed == citizenNewsData ? _self.citizenNewsData : citizenNewsData // ignore: cast_nullable_to_non_nullable
as CitizenNewsData?,localizationUpdateInfo: freezed == localizationUpdateInfo ? _self.localizationUpdateInfo : localizationUpdateInfo // ignore: cast_nullable_to_non_nullable
as MapEntry<String, bool>?,scServerStatus: freezed == scServerStatus ? _self._scServerStatus : scServerStatus // ignore: cast_nullable_to_non_nullable
as List?,countdownFestivalListData: freezed == countdownFestivalListData ? _self._countdownFestivalListData : countdownFestivalListData // ignore: cast_nullable_to_non_nullable
as List<CountdownFestivalItemData>?,isGameRunning: null == isGameRunning ? _self._isGameRunning : isGameRunning // ignore: cast_nullable_to_non_nullable
@ -345,7 +338,19 @@ as Map<String, bool>,
));
}
/// Create a copy of HomeUIModelState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$CitizenNewsDataCopyWith<$Res>? get citizenNewsData {
if (_self.citizenNewsData == null) {
return null;
}
return $CitizenNewsDataCopyWith<$Res>(_self.citizenNewsData!, (value) {
return _then(_self.copyWith(citizenNewsData: value));
});
}
}
// dart format on

View File

@ -41,7 +41,7 @@ final class HomeUIModelProvider
}
}
String _$homeUIModelHash() => r'84eb149f999237410a7e0a95b74bd5729c2726d4';
String _$homeUIModelHash() => r'9dc8191f358c2d8e21ed931b3755e08ce394558e';
abstract class _$HomeUIModel extends $Notifier<HomeUIModelState> {
HomeUIModelState build();

View File

@ -41,7 +41,7 @@ final class SettingsUIModelProvider
}
}
String _$settingsUIModelHash() => r'd19104d924f018a9230548d0372692fc344adacd';
String _$settingsUIModelHash() => r'5c08c56bf5464ef44bee8edb8c18c08d4217f135';
abstract class _$SettingsUIModel extends $Notifier<SettingsUIState> {
SettingsUIState build();

View File

@ -10,12 +10,13 @@ class FlowNumberText extends HookConsumerWidget {
final TextStyle? style;
final Curve curve;
FlowNumberText(
{super.key,
required this.targetValue,
this.duration = const Duration(seconds: 1),
this.style,
this.curve = Curves.bounceOut});
FlowNumberText({
super.key,
required this.targetValue,
this.duration = const Duration(seconds: 1),
this.style,
this.curve = Curves.bounceOut,
});
final _formatter = NumberFormat.decimalPattern();
@ -46,9 +47,6 @@ class FlowNumberText extends HookConsumerWidget {
return timer.value?.cancel;
}, [targetValue]);
return Text(
_formatter.format(value.value.toInt()),
style: style,
);
return Text(_formatter.format(value.value.toInt()), style: style);
}
}

View File

@ -20,19 +20,14 @@ class GridItemAnimator extends HookWidget {
@override
Widget build(BuildContext context) {
//
final animationController = useAnimationController(
duration: duration,
);
final animationController = useAnimationController(duration: duration);
//
final opacityAnimation = useAnimation(
Tween<double>(
begin: 0.0, //
end: 1.0, //
).animate(CurvedAnimation(
parent: animationController,
curve: Curves.easeOut,
)),
).animate(CurvedAnimation(parent: animationController, curve: Curves.easeOut)),
);
//
@ -40,23 +35,24 @@ class GridItemAnimator extends HookWidget {
Tween<double>(
begin: 1.0, //
end: 0.0, //
).animate(CurvedAnimation(
parent: animationController,
curve: Curves.easeOutCubic,
)),
).animate(CurvedAnimation(parent: animationController, curve: Curves.easeOutCubic)),
);
//
useEffect(() {
//
final delay = delayPerItem * index;
bool cancelled = false;
Future.delayed(delay, () {
if (animationController.status != AnimationStatus.completed) {
if (!cancelled && animationController.status != AnimationStatus.completed) {
animationController.forward();
}
});
return null;
return () {
cancelled = true;
};
}, const []);
//

115
lib/widgets/src/swiper.dart Normal file
View File

@ -0,0 +1,115 @@
import 'package:card_swiper/card_swiper.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_tilt/flutter_tilt.dart';
class HoverSwiper extends HookWidget {
const HoverSwiper({
super.key,
required this.itemCount,
required this.itemBuilder,
this.autoplayDelay = 3000,
this.paginationActiveSize = 8.0,
this.controlSize = 24,
this.controlPadding = const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
});
final int itemCount;
final IndexedWidgetBuilder itemBuilder;
final double paginationActiveSize;
final double controlSize;
final EdgeInsets controlPadding;
final int autoplayDelay;
@override
Widget build(BuildContext context) {
final isHovered = useState(false);
final controller = useMemoized(() => SwiperController());
useEffect(() {
return controller.dispose;
}, [controller]);
return MouseRegion(
onEnter: (_) {
isHovered.value = true;
controller.stopAutoplay();
},
onExit: (_) {
isHovered.value = false;
controller.startAutoplay();
},
child: Stack(
children: [
Tilt(
shadowConfig: const ShadowConfig(maxIntensity: .3),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
child: Swiper(
controller: controller,
itemCount: itemCount,
itemBuilder: itemBuilder,
autoplay: true,
autoplayDelay: autoplayDelay,
),
),
// Left control button
_buildControlButton(
isHovered: isHovered.value,
position: 'left',
onTap: () => controller.previous(),
icon: FluentIcons.chevron_left,
),
// Right control button
_buildControlButton(
isHovered: isHovered.value,
position: 'right',
onTap: () => controller.next(),
icon: FluentIcons.chevron_right,
),
],
),
);
}
/// /
Widget _buildControlButton({
required bool isHovered,
required String position,
required VoidCallback onTap,
required IconData icon,
}) {
final isLeft = position == 'left';
return Positioned(
left: isLeft ? 0 : null,
right: isLeft ? null : 0,
top: 0,
bottom: 0,
child: AnimatedOpacity(
opacity: isHovered ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: IgnorePointer(
ignoring: !isHovered,
child: Center(
child: Padding(
padding: controlPadding,
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: .3),
borderRadius: BorderRadius.circular(4),
),
child: Icon(icon, size: controlSize, color: Colors.white.withValues(alpha: .8)),
),
),
),
),
),
),
),
);
}
}

View File

@ -17,53 +17,42 @@ export 'src/cache_image.dart';
export 'src/countdown_time_text.dart';
export 'src/cache_svg_image.dart';
export 'src/grid_item_animator.dart';
export 'src/swiper.dart';
export '../common/utils/async.dart';
export '../common/utils/base_utils.dart';
export 'package:starcitizen_doctor/generated/l10n.dart';
Widget makeLoading(
BuildContext context, {
double? width,
}) {
Widget makeLoading(BuildContext context, {double? width}) {
width ??= 30;
return Center(
child: SizedBox(
width: width,
height: width,
child: const ProgressRing(),
),
child: SizedBox(width: width, height: width, child: const ProgressRing()),
);
}
Widget makeDefaultPage(BuildContext context,
{Widget? titleRow,
List<Widget>? actions,
Widget? content,
bool automaticallyImplyLeading = true,
String title = "",
bool useBodyContainer = false}) {
Widget makeDefaultPage(
BuildContext context, {
Widget? titleRow,
List<Widget>? actions,
Widget? content,
bool automaticallyImplyLeading = true,
String title = "",
bool useBodyContainer = false,
}) {
return NavigationView(
appBar: NavigationAppBar(
automaticallyImplyLeading: automaticallyImplyLeading,
title: DragToMoveArea(
child: titleRow ??
Column(
children: [
Expanded(
child: Row(
children: [
Text(title),
],
),
)
],
),
),
actions: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [...?actions, const WindowButtons()],
)),
automaticallyImplyLeading: automaticallyImplyLeading,
title: DragToMoveArea(
child:
titleRow ??
Column(
children: [
Expanded(child: Row(children: [Text(title)])),
],
),
),
actions: Row(mainAxisAlignment: MainAxisAlignment.end, children: [...?actions, const WindowButtons()]),
),
content: useBodyContainer
? Container(
decoration: BoxDecoration(
@ -85,55 +74,63 @@ class WindowButtons extends StatelessWidget {
return SizedBox(
width: 138,
height: 50,
child: WindowCaption(
brightness: theme.brightness,
backgroundColor: Colors.transparent,
),
child: WindowCaption(brightness: theme.brightness, backgroundColor: Colors.transparent),
);
}
}
List<Widget> makeMarkdownView(String description, {String? attachmentsUrl}) {
return MarkdownGenerator().buildWidgets(description,
config: MarkdownConfig(configs: [
LinkConfig(onTap: (url) {
if (url.startsWith("/") && attachmentsUrl != null) {
url = "$attachmentsUrl/$url";
}
launchUrlString(url);
}),
ImgConfig(builder: (String url, Map<String, String> attributes) {
if (url.startsWith("/") && attachmentsUrl != null) {
url = "$attachmentsUrl/$url";
}
return ExtendedImage.network(
url,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const ProgressRing(),
const SizedBox(height: 12),
Text(S.current.app_common_loading_images)
],
return MarkdownGenerator().buildWidgets(
description,
config: MarkdownConfig(
configs: [
LinkConfig(
onTap: (url) {
if (url.startsWith("/") && attachmentsUrl != null) {
url = "$attachmentsUrl/$url";
}
launchUrlString(url);
},
),
ImgConfig(
builder: (String url, Map<String, String> attributes) {
if (url.startsWith("/") && attachmentsUrl != null) {
url = "$attachmentsUrl/$url";
}
return ExtendedImage.network(
url,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const ProgressRing(),
const SizedBox(height: 12),
Text(S.current.app_common_loading_images),
],
),
),
),
);
case LoadState.completed:
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
);
case LoadState.failed:
return Text("Loading Image error $url");
}
},
);
})
]));
);
case LoadState.completed:
return ExtendedRawImage(image: state.extendedImageInfo?.image);
case LoadState.failed:
return Button(
onPressed: () {
launchUrlString(url);
},
child: Text("Loading Image error $url"),
);
}
},
);
},
),
],
),
);
}
ColorFilter makeSvgColor(Color color) {
@ -142,22 +139,20 @@ ColorFilter makeSvgColor(Color color) {
CustomTransitionPage<T> myPageBuilder<T>(BuildContext context, GoRouterState state, Widget child) {
return CustomTransitionPage(
child: child,
transitionDuration: const Duration(milliseconds: 150),
reverseTransitionDuration: const Duration(milliseconds: 150),
transitionsBuilder:
(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0),
).animate(CurvedAnimation(
parent: animation,
curve: Curves.easeInOut,
)),
child: child,
);
});
child: child,
transitionDuration: const Duration(milliseconds: 150),
reverseTransitionDuration: const Duration(milliseconds: 150),
transitionsBuilder:
(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: const Offset(0.0, 0.0),
).animate(CurvedAnimation(parent: animation, curve: Curves.easeInOut)),
child: child,
);
},
);
}
class LoadingWidget<T> extends HookConsumerWidget {
@ -192,9 +187,7 @@ class LoadingWidget<T> extends HookConsumerWidget {
onPressed: () {
_loadData(dataState, errorMsg);
},
child: Center(
child: Text(errorMsg.value),
),
child: Center(child: Text(errorMsg.value)),
);
}
if (dataState.value == null && data == null) return makeLoading(context);

View File

@ -306,14 +306,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0+7.7.0"
dart_rss:
dependency: "direct main"
description:
name: dart_rss
sha256: "73539d4b7153b47beef8b51763ca55dcb6fc0bb412b29e0f5e74e93fabfd1ac6"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
dart_style:
dependency: transitive
description:
@ -1610,7 +1602,7 @@ packages:
source: hosted
version: "1.1.0"
xml:
dependency: "direct main"
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"

View File

@ -47,9 +47,7 @@ dependencies:
freezed_annotation: ^3.1.0
meta: ^1.16.0
hexcolor: ^3.0.1
dart_rss: ^3.0.3
html: ^0.15.6
xml: ^6.6.1
fixnum: ^1.1.1
rust_builder:
path: rust_builder