From 0876ab9b7499bda2ffa3ceccad4bee73bfdf6fbe Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 25 Dec 2025 00:29:11 +0800 Subject: [PATCH] :lipstick: Optimized fortune saying and dashboard clock --- assets/i18n/en-US.json | 5 +- assets/i18n/zh-CN.json | 1 + lib/models/activity.dart | 3 +- lib/models/activity.freezed.dart | 49 +++--- lib/models/activity.g.dart | 4 +- lib/models/fortune.dart | 16 ++ lib/models/fortune.freezed.dart | 277 +++++++++++++++++++++++++++++++ lib/models/fortune.g.dart | 21 +++ lib/pods/config.g.dart | 2 +- lib/screens/dashboard/dash.dart | 136 +++++++-------- lib/widgets/check_in.dart | 41 ++++- lib/widgets/check_in.g.dart | 81 ++++++++- 12 files changed, 539 insertions(+), 97 deletions(-) create mode 100644 lib/models/fortune.dart create mode 100644 lib/models/fortune.freezed.dart create mode 100644 lib/models/fortune.g.dart diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 079a41b1..2a16b85a 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -1021,6 +1021,7 @@ "noResultsFound": "No results found", "toggleFilters": "Toggle filters", "notableDayNext": "{} is in", + "notableDayToday": "{} is today!", "expandPoll": "Expand Poll", "collapsePoll": "Collapse Poll", "embedView": "Embed View", @@ -1539,5 +1540,7 @@ "settingsNotifyWithHaptic": "Notification with Haptic Feedback", "settingsDashSearchEngine": "Search Engine for web", "settingsDashSearchEngineHelper": "Use %s as the placeholder for the query.", - "settingsDefaultScreen": "Default Screen" + "settingsDefaultScreen": "Default Screen", + "notableDayChristmas": "Christmas", + "notableDayNewYear": "New Year" } diff --git a/assets/i18n/zh-CN.json b/assets/i18n/zh-CN.json index fd83f2da..7d1e6d90 100644 --- a/assets/i18n/zh-CN.json +++ b/assets/i18n/zh-CN.json @@ -999,6 +999,7 @@ "noResultsFound": "未找到结果", "toggleFilters": "切换过滤器", "notableDayNext": "距离 {} 还有", + "notableDayToday": "今天是 {}!", "expandPoll": "展开投票", "collapsePoll": "折叠投票", "embedView": "嵌入视图", diff --git a/lib/models/activity.dart b/lib/models/activity.dart index 18efacfd..3ebc9717 100644 --- a/lib/models/activity.dart +++ b/lib/models/activity.dart @@ -10,7 +10,8 @@ sealed class SnNotableDay with _$SnNotableDay { required DateTime date, required String localName, required String globalName, - required String countryCode, + required String? countryCode, + required String? localizableKey, required List holidays, }) = _SnNotableDay; diff --git a/lib/models/activity.freezed.dart b/lib/models/activity.freezed.dart index f35da93d..ce609a0e 100644 --- a/lib/models/activity.freezed.dart +++ b/lib/models/activity.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$SnNotableDay { - DateTime get date; String get localName; String get globalName; String get countryCode; List get holidays; + DateTime get date; String get localName; String get globalName; String? get countryCode; String? get localizableKey; List get holidays; /// Create a copy of SnNotableDay /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -28,16 +28,16 @@ $SnNotableDayCopyWith get copyWith => _$SnNotableDayCopyWithImpl Object.hash(runtimeType,date,localName,globalName,countryCode,const DeepCollectionEquality().hash(holidays)); +int get hashCode => Object.hash(runtimeType,date,localName,globalName,countryCode,localizableKey,const DeepCollectionEquality().hash(holidays)); @override String toString() { - return 'SnNotableDay(date: $date, localName: $localName, globalName: $globalName, countryCode: $countryCode, holidays: $holidays)'; + return 'SnNotableDay(date: $date, localName: $localName, globalName: $globalName, countryCode: $countryCode, localizableKey: $localizableKey, holidays: $holidays)'; } @@ -48,7 +48,7 @@ abstract mixin class $SnNotableDayCopyWith<$Res> { factory $SnNotableDayCopyWith(SnNotableDay value, $Res Function(SnNotableDay) _then) = _$SnNotableDayCopyWithImpl; @useResult $Res call({ - DateTime date, String localName, String globalName, String countryCode, List holidays + DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List holidays }); @@ -65,13 +65,14 @@ class _$SnNotableDayCopyWithImpl<$Res> /// Create a copy of SnNotableDay /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? date = null,Object? localName = null,Object? globalName = null,Object? countryCode = null,Object? holidays = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? date = null,Object? localName = null,Object? globalName = null,Object? countryCode = freezed,Object? localizableKey = freezed,Object? holidays = null,}) { return _then(_self.copyWith( date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable as DateTime,localName: null == localName ? _self.localName : localName // ignore: cast_nullable_to_non_nullable as String,globalName: null == globalName ? _self.globalName : globalName // ignore: cast_nullable_to_non_nullable -as String,countryCode: null == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable -as String,holidays: null == holidays ? _self.holidays : holidays // ignore: cast_nullable_to_non_nullable +as String,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable +as String?,localizableKey: freezed == localizableKey ? _self.localizableKey : localizableKey // ignore: cast_nullable_to_non_nullable +as String?,holidays: null == holidays ? _self.holidays : holidays // ignore: cast_nullable_to_non_nullable as List, )); } @@ -154,10 +155,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( DateTime date, String localName, String globalName, String countryCode, List holidays)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List holidays)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SnNotableDay() when $default != null: -return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.holidays);case _: +return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.localizableKey,_that.holidays);case _: return orElse(); } @@ -175,10 +176,10 @@ return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_t /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( DateTime date, String localName, String globalName, String countryCode, List holidays) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List holidays) $default,) {final _that = this; switch (_that) { case _SnNotableDay(): -return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.holidays);} +return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.localizableKey,_that.holidays);} } /// A variant of `when` that fallback to returning `null` /// @@ -192,10 +193,10 @@ return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_t /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( DateTime date, String localName, String globalName, String countryCode, List holidays)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List holidays)? $default,) {final _that = this; switch (_that) { case _SnNotableDay() when $default != null: -return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.holidays);case _: +return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.localizableKey,_that.holidays);case _: return null; } @@ -207,13 +208,14 @@ return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_t @JsonSerializable() class _SnNotableDay implements SnNotableDay { - const _SnNotableDay({required this.date, required this.localName, required this.globalName, required this.countryCode, required final List holidays}): _holidays = holidays; + const _SnNotableDay({required this.date, required this.localName, required this.globalName, required this.countryCode, required this.localizableKey, required final List holidays}): _holidays = holidays; factory _SnNotableDay.fromJson(Map json) => _$SnNotableDayFromJson(json); @override final DateTime date; @override final String localName; @override final String globalName; -@override final String countryCode; +@override final String? countryCode; +@override final String? localizableKey; final List _holidays; @override List get holidays { if (_holidays is EqualUnmodifiableListView) return _holidays; @@ -235,16 +237,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnNotableDay&&(identical(other.date, date) || other.date == date)&&(identical(other.localName, localName) || other.localName == localName)&&(identical(other.globalName, globalName) || other.globalName == globalName)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other._holidays, _holidays)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnNotableDay&&(identical(other.date, date) || other.date == date)&&(identical(other.localName, localName) || other.localName == localName)&&(identical(other.globalName, globalName) || other.globalName == globalName)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&(identical(other.localizableKey, localizableKey) || other.localizableKey == localizableKey)&&const DeepCollectionEquality().equals(other._holidays, _holidays)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,date,localName,globalName,countryCode,const DeepCollectionEquality().hash(_holidays)); +int get hashCode => Object.hash(runtimeType,date,localName,globalName,countryCode,localizableKey,const DeepCollectionEquality().hash(_holidays)); @override String toString() { - return 'SnNotableDay(date: $date, localName: $localName, globalName: $globalName, countryCode: $countryCode, holidays: $holidays)'; + return 'SnNotableDay(date: $date, localName: $localName, globalName: $globalName, countryCode: $countryCode, localizableKey: $localizableKey, holidays: $holidays)'; } @@ -255,7 +257,7 @@ abstract mixin class _$SnNotableDayCopyWith<$Res> implements $SnNotableDayCopyWi factory _$SnNotableDayCopyWith(_SnNotableDay value, $Res Function(_SnNotableDay) _then) = __$SnNotableDayCopyWithImpl; @override @useResult $Res call({ - DateTime date, String localName, String globalName, String countryCode, List holidays + DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List holidays }); @@ -272,13 +274,14 @@ class __$SnNotableDayCopyWithImpl<$Res> /// Create a copy of SnNotableDay /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? date = null,Object? localName = null,Object? globalName = null,Object? countryCode = null,Object? holidays = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? date = null,Object? localName = null,Object? globalName = null,Object? countryCode = freezed,Object? localizableKey = freezed,Object? holidays = null,}) { return _then(_SnNotableDay( date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable as DateTime,localName: null == localName ? _self.localName : localName // ignore: cast_nullable_to_non_nullable as String,globalName: null == globalName ? _self.globalName : globalName // ignore: cast_nullable_to_non_nullable -as String,countryCode: null == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable -as String,holidays: null == holidays ? _self._holidays : holidays // ignore: cast_nullable_to_non_nullable +as String,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable +as String?,localizableKey: freezed == localizableKey ? _self.localizableKey : localizableKey // ignore: cast_nullable_to_non_nullable +as String?,holidays: null == holidays ? _self._holidays : holidays // ignore: cast_nullable_to_non_nullable as List, )); } diff --git a/lib/models/activity.g.dart b/lib/models/activity.g.dart index 87f8a7d6..f4f83716 100644 --- a/lib/models/activity.g.dart +++ b/lib/models/activity.g.dart @@ -11,7 +11,8 @@ _SnNotableDay _$SnNotableDayFromJson(Map json) => date: DateTime.parse(json['date'] as String), localName: json['local_name'] as String, globalName: json['global_name'] as String, - countryCode: json['country_code'] as String, + countryCode: json['country_code'] as String?, + localizableKey: json['localizable_key'] as String?, holidays: (json['holidays'] as List) .map((e) => (e as num).toInt()) .toList(), @@ -23,6 +24,7 @@ Map _$SnNotableDayToJson(_SnNotableDay instance) => 'local_name': instance.localName, 'global_name': instance.globalName, 'country_code': instance.countryCode, + 'localizable_key': instance.localizableKey, 'holidays': instance.holidays, }; diff --git a/lib/models/fortune.dart b/lib/models/fortune.dart new file mode 100644 index 00000000..bcdd9fa2 --- /dev/null +++ b/lib/models/fortune.dart @@ -0,0 +1,16 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'fortune.g.dart'; +part 'fortune.freezed.dart'; + +@freezed +sealed class SnFortuneSaying with _$SnFortuneSaying { + const factory SnFortuneSaying({ + required String content, + required String source, + required String language, + }) = _SnFortuneSaying; + + factory SnFortuneSaying.fromJson(Map json) => + _$SnFortuneSayingFromJson(json); +} diff --git a/lib/models/fortune.freezed.dart b/lib/models/fortune.freezed.dart new file mode 100644 index 00000000..2b8dbf82 --- /dev/null +++ b/lib/models/fortune.freezed.dart @@ -0,0 +1,277 @@ +// 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 'fortune.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$SnFortuneSaying { + + String get content; String get source; String get language; +/// Create a copy of SnFortuneSaying +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SnFortuneSayingCopyWith get copyWith => _$SnFortuneSayingCopyWithImpl(this as SnFortuneSaying, _$identity); + + /// Serializes this SnFortuneSaying to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnFortuneSaying&&(identical(other.content, content) || other.content == content)&&(identical(other.source, source) || other.source == source)&&(identical(other.language, language) || other.language == language)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,content,source,language); + +@override +String toString() { + return 'SnFortuneSaying(content: $content, source: $source, language: $language)'; +} + + +} + +/// @nodoc +abstract mixin class $SnFortuneSayingCopyWith<$Res> { + factory $SnFortuneSayingCopyWith(SnFortuneSaying value, $Res Function(SnFortuneSaying) _then) = _$SnFortuneSayingCopyWithImpl; +@useResult +$Res call({ + String content, String source, String language +}); + + + + +} +/// @nodoc +class _$SnFortuneSayingCopyWithImpl<$Res> + implements $SnFortuneSayingCopyWith<$Res> { + _$SnFortuneSayingCopyWithImpl(this._self, this._then); + + final SnFortuneSaying _self; + final $Res Function(SnFortuneSaying) _then; + +/// Create a copy of SnFortuneSaying +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? content = null,Object? source = null,Object? language = null,}) { + return _then(_self.copyWith( +content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable +as String,source: null == source ? _self.source : source // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// Adds pattern-matching-related methods to [SnFortuneSaying]. +extension SnFortuneSayingPatterns on SnFortuneSaying { +/// 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 Function( _SnFortuneSaying value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SnFortuneSaying() 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 Function( _SnFortuneSaying value) $default,){ +final _that = this; +switch (_that) { +case _SnFortuneSaying(): +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? Function( _SnFortuneSaying value)? $default,){ +final _that = this; +switch (_that) { +case _SnFortuneSaying() 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 Function( String content, String source, String language)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SnFortuneSaying() when $default != null: +return $default(_that.content,_that.source,_that.language);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 Function( String content, String source, String language) $default,) {final _that = this; +switch (_that) { +case _SnFortuneSaying(): +return $default(_that.content,_that.source,_that.language);} +} +/// 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? Function( String content, String source, String language)? $default,) {final _that = this; +switch (_that) { +case _SnFortuneSaying() when $default != null: +return $default(_that.content,_that.source,_that.language);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SnFortuneSaying implements SnFortuneSaying { + const _SnFortuneSaying({required this.content, required this.source, required this.language}); + factory _SnFortuneSaying.fromJson(Map json) => _$SnFortuneSayingFromJson(json); + +@override final String content; +@override final String source; +@override final String language; + +/// Create a copy of SnFortuneSaying +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SnFortuneSayingCopyWith<_SnFortuneSaying> get copyWith => __$SnFortuneSayingCopyWithImpl<_SnFortuneSaying>(this, _$identity); + +@override +Map toJson() { + return _$SnFortuneSayingToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnFortuneSaying&&(identical(other.content, content) || other.content == content)&&(identical(other.source, source) || other.source == source)&&(identical(other.language, language) || other.language == language)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,content,source,language); + +@override +String toString() { + return 'SnFortuneSaying(content: $content, source: $source, language: $language)'; +} + + +} + +/// @nodoc +abstract mixin class _$SnFortuneSayingCopyWith<$Res> implements $SnFortuneSayingCopyWith<$Res> { + factory _$SnFortuneSayingCopyWith(_SnFortuneSaying value, $Res Function(_SnFortuneSaying) _then) = __$SnFortuneSayingCopyWithImpl; +@override @useResult +$Res call({ + String content, String source, String language +}); + + + + +} +/// @nodoc +class __$SnFortuneSayingCopyWithImpl<$Res> + implements _$SnFortuneSayingCopyWith<$Res> { + __$SnFortuneSayingCopyWithImpl(this._self, this._then); + + final _SnFortuneSaying _self; + final $Res Function(_SnFortuneSaying) _then; + +/// Create a copy of SnFortuneSaying +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? content = null,Object? source = null,Object? language = null,}) { + return _then(_SnFortuneSaying( +content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable +as String,source: null == source ? _self.source : source // ignore: cast_nullable_to_non_nullable +as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/models/fortune.g.dart b/lib/models/fortune.g.dart new file mode 100644 index 00000000..a293dbd6 --- /dev/null +++ b/lib/models/fortune.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'fortune.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_SnFortuneSaying _$SnFortuneSayingFromJson(Map json) => + _SnFortuneSaying( + content: json['content'] as String, + source: json['source'] as String, + language: json['language'] as String, + ); + +Map _$SnFortuneSayingToJson(_SnFortuneSaying instance) => + { + 'content': instance.content, + 'source': instance.source, + 'language': instance.language, + }; diff --git a/lib/pods/config.g.dart b/lib/pods/config.g.dart index 73208eab..fcfea624 100644 --- a/lib/pods/config.g.dart +++ b/lib/pods/config.g.dart @@ -65,7 +65,7 @@ final class AppSettingsNotifierProvider } String _$appSettingsNotifierHash() => - r'7e311f421fb3a24946f95b7d207821151ba5a326'; + r'ef10d95a9f22e891ad6f5e0225e31508b3eb038e'; abstract class _$AppSettingsNotifier extends $Notifier { AppSettings build(); diff --git a/lib/screens/dashboard/dash.dart b/lib/screens/dashboard/dash.dart index 18deb064..cf8096a7 100644 --- a/lib/screens/dashboard/dash.dart +++ b/lib/screens/dashboard/dash.dart @@ -21,6 +21,7 @@ import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/notification_tile.dart'; import 'package:island/widgets/post/post_featured.dart'; import 'package:island/widgets/check_in.dart'; +import 'package:island/models/activity.dart'; import 'package:island/screens/notification.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:slide_countdown/slide_countdown.dart'; @@ -229,7 +230,7 @@ class ClockCard extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final time = useState(DateTime.now()); final timer = useRef(null); - final nextNotableDay = ref.watch(nextNotableDayProvider); + final notableDay = ref.watch(recentNotableDayProvider); // Determine icon based on time of day final int hour = time.value.hour; @@ -301,23 +302,13 @@ class ClockCard extends HookConsumerWidget { Row( spacing: 5, children: [ - Text('notableDayNext') - .tr( - args: [ - nextNotableDay.value?.localName ?? 'idk', - ], - ) - .fontSize(12), - if (nextNotableDay.value != null) - SlideCountdown( - decoration: const BoxDecoration(), - style: const TextStyle(fontSize: 12), - separatorStyle: const TextStyle(fontSize: 12), - padding: EdgeInsets.zero, - duration: nextNotableDay.value?.date.difference( - DateTime.now(), - ), - ), + notableDay.when( + data: (day) => _buildNotableDayText(context, day!), + error: (err, _) => + Text(err.toString()).fontSize(12), + loading: () => + const Text('loading').tr().fontSize(12), + ), ], ), ], @@ -330,6 +321,42 @@ class ClockCard extends HookConsumerWidget { ), ); } + + Widget _buildNotableDayText(BuildContext context, SnNotableDay notableDay) { + final today = DateTime.now(); + final isToday = + notableDay.date.year == today.year && + notableDay.date.month == today.month && + notableDay.date.day == today.day; + + if (isToday) { + return Row( + spacing: 5, + children: [ + Text('notableDayToday').tr(args: [notableDay.localName]).fontSize(12), + Icon( + Symbols.celebration_rounded, + size: 16, + color: Theme.of(context).colorScheme.primary, + ), + ], + ); + } else { + return Row( + spacing: 5, + children: [ + Text('notableDayNext').tr(args: [notableDay.localName]).fontSize(12), + SlideCountdown( + decoration: const BoxDecoration(), + style: const TextStyle(fontSize: 12), + separatorStyle: const TextStyle(fontSize: 12), + padding: EdgeInsets.zero, + duration: notableDay.date.difference(DateTime.now()), + ), + ], + ); + } + } } class NotificationsCard extends HookConsumerWidget { @@ -493,65 +520,38 @@ class ChatListCard extends HookConsumerWidget { } } -class FortuneCard extends HookWidget { +class FortuneCard extends HookConsumerWidget { const FortuneCard({super.key}); @override - Widget build(BuildContext context) { - final fortune = useMemoized(() { - const fortunes = [ - {'text': '有的人活着,但他已经死了。', 'author': '—— 鲁迅'}, - {'text': '天行健,君子以自强不息。', 'author': '—— 《周易》'}, - {'text': '路漫漫其修远兮,吾将上下而求索。', 'author': '—— 屈原'}, - {'text': '学海无涯苦作舟。', 'author': '—— 韩愈'}, - {'text': '天道酬勤。', 'author': '—— 古语'}, - {'text': '书山有路勤为径,学海无涯苦作舟。', 'author': '—— 韩愈'}, - {'text': '莫等闲,白了少年头,空悲切。', 'author': '—— 岳飞'}, - { - 'text': 'The best way to predict the future is to create it.', - 'author': '— Peter Drucker', - }, - {'text': 'Fortune favors the bold.', 'author': '— Virgil'}, - { - 'text': 'A journey of a thousand miles begins with a single step.', - 'author': '— Lao Tzu', - }, - { - 'text': 'The only way to do great work is to love what you do.', - 'author': '— Steve Jobs', - }, - { - 'text': 'Believe you can and you\'re halfway there.', - 'author': '— Theodore Roosevelt', - }, - { - 'text': - 'The future belongs to those who believe in the beauty of their dreams.', - 'author': '— Eleanor Roosevelt', - }, - ]; - return fortunes[math.Random().nextInt(fortunes.length)]; - }); + Widget build(BuildContext context, WidgetRef ref) { + final fortuneAsync = ref.watch(randomFortuneSayingProvider); return Card( margin: EdgeInsets.zero, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(12)), ), - child: Row( - spacing: 8, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - fortune['text']!, - maxLines: 2, - overflow: TextOverflow.fade, - ), - ), - Text(fortune['author']!).bold(), - ], - ).padding(horizontal: 16), + child: fortuneAsync.when( + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stack) => Center(child: Text('Error: $error')), + data: (fortune) { + return Row( + spacing: 8, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + fortune.content, + maxLines: 2, + overflow: TextOverflow.fade, + ), + ), + Text('—— ${fortune.source}').bold(), + ], + ).padding(horizontal: 16); + }, + ), ).height(48); } } diff --git a/lib/widgets/check_in.dart b/lib/widgets/check_in.dart index 1e7abec1..5ef15839 100644 --- a/lib/widgets/check_in.dart +++ b/lib/widgets/check_in.dart @@ -8,6 +8,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/activity.dart'; +import 'package:island/models/fortune.dart'; import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/screens/auth/captcha.dart'; @@ -42,12 +43,50 @@ Future nextNotableDay(Ref ref) async { final client = ref.watch(apiClientProvider); try { final resp = await client.get('/pass/notable/me/next'); - return SnNotableDay.fromJson(resp.data); + final day = SnNotableDay.fromJson(resp.data); + if (day.localizableKey != null) { + final key = 'notableDay${day.localizableKey}'; + if (key.trExists()) { + return day.copyWith( + localName: key.tr(), + date: day.date.toLocal().copyWith(hour: 0, second: 0), + ); + } + } + return day.copyWith(date: day.date.toLocal().copyWith(hour: 0, second: 0)); } catch (err) { return null; } } +@riverpod +Future recentNotableDay(Ref ref) async { + final client = ref.watch(apiClientProvider); + try { + final resp = await client.get('/pass/notable/me/recent'); + final day = SnNotableDay.fromJson(resp.data[0]); + if (day.localizableKey != null) { + final key = 'notableDay${day.localizableKey}'; + if (key.trExists()) { + return day.copyWith( + localName: key.tr(), + date: day.date.toLocal().copyWith(hour: 0, second: 0), + ); + } + } + return day.copyWith(date: day.date.toLocal().copyWith(hour: 0, second: 0)); + } catch (err) { + return null; + } +} + +@riverpod +Future randomFortuneSaying(Ref ref) async { + final client = ref.watch(apiClientProvider); + final resp = await client.get('/pass/fortune/random'); + return SnFortuneSaying.fromJson(resp.data[0]); +} + class CheckInWidget extends HookConsumerWidget { final EdgeInsets? margin; final VoidCallback? onChecked; diff --git a/lib/widgets/check_in.g.dart b/lib/widgets/check_in.g.dart index 154cb88a..b66eeceb 100644 --- a/lib/widgets/check_in.g.dart +++ b/lib/widgets/check_in.g.dart @@ -86,4 +86,83 @@ final class NextNotableDayProvider } } -String _$nextNotableDayHash() => r'c8404308f6b0f581cc7df251bce8f3c5ac130245'; +String _$nextNotableDayHash() => r'60d0546a086bdcb89c433c38133eb4197e4fb0a6'; + +@ProviderFor(recentNotableDay) +const recentNotableDayProvider = RecentNotableDayProvider._(); + +final class RecentNotableDayProvider + extends + $FunctionalProvider< + AsyncValue, + SnNotableDay?, + FutureOr + > + with $FutureModifier, $FutureProvider { + const RecentNotableDayProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'recentNotableDayProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$recentNotableDayHash(); + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return recentNotableDay(ref); + } +} + +String _$recentNotableDayHash() => r'780d0f0747d753c5d535d9c2413f8e68d457d974'; + +@ProviderFor(randomFortuneSaying) +const randomFortuneSayingProvider = RandomFortuneSayingProvider._(); + +final class RandomFortuneSayingProvider + extends + $FunctionalProvider< + AsyncValue, + SnFortuneSaying, + FutureOr + > + with $FutureModifier, $FutureProvider { + const RandomFortuneSayingProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'randomFortuneSayingProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$randomFortuneSayingHash(); + + @$internal + @override + $FutureProviderElement $createElement( + $ProviderPointer pointer, + ) => $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return randomFortuneSaying(ref); + } +} + +String _$randomFortuneSayingHash() => + r'861378dba8021e8555b568fb8e0390b2b24056f6';