From d678cc571a25cb62da3479330c1dcc5947d90e5f Mon Sep 17 00:00:00 2001
From: LittleSheep <littlesheep.code@hotmail.com>
Date: Sat, 10 May 2025 02:24:58 +0800
Subject: [PATCH] :recycle: Update event calander loading

---
 lib/screens/account/me/event_calendar.dart    | 153 ++++++++++-------
 .../account/me/event_calendar.freezed.dart    | 148 ++++++++++++++++
 lib/screens/account/me/event_calendar.g.dart  | 161 ++++++++++++++++--
 3 files changed, 380 insertions(+), 82 deletions(-)
 create mode 100644 lib/screens/account/me/event_calendar.freezed.dart

diff --git a/lib/screens/account/me/event_calendar.dart b/lib/screens/account/me/event_calendar.dart
index d613721..4e28013 100644
--- a/lib/screens/account/me/event_calendar.dart
+++ b/lib/screens/account/me/event_calendar.dart
@@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
 import 'package:gap/gap.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:island/models/activity.dart';
@@ -13,11 +14,24 @@ import 'package:styled_widget/styled_widget.dart';
 import 'package:table_calendar/table_calendar.dart';
 
 part 'event_calendar.g.dart';
+part 'event_calendar.freezed.dart';
+
+@freezed
+abstract class EventCalendarQuery with _$EventCalendarQuery {
+  const factory EventCalendarQuery({
+    required String? uname,
+    required int year,
+    required int month,
+  }) = _EventCalendarQuery;
+}
 
 @riverpod
-Future<List<SnEventCalendarEntry>> myselfAccountEventCalendar(Ref ref) async {
+Future<List<SnEventCalendarEntry>> accountEventCalendar(
+  Ref ref,
+  EventCalendarQuery query,
+) async {
   final client = ref.watch(apiClientProvider);
-  final resp = await client.get('/accounts/me/calendar');
+  final resp = await client.get('/accounts/${query.uname ?? 'me'}/calendar');
   return resp.data
       .map((e) => SnEventCalendarEntry.fromJson(e))
       .cast<SnEventCalendarEntry>()
@@ -30,10 +44,21 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
 
   @override
   Widget build(BuildContext context, WidgetRef ref) {
-    final events = ref.watch(myselfAccountEventCalendarProvider);
+    final selectedMonth = useState(DateTime.now().month);
+    final selectedYear = useState(DateTime.now().year);
 
     final selectedDay = useState(DateTime.now());
 
+    final events = ref.watch(
+      accountEventCalendarProvider(
+        EventCalendarQuery(
+          uname: 'me',
+          year: selectedYear.value,
+          month: selectedMonth.value,
+        ),
+      ),
+    );
+
     return AppScaffold(
       appBar: AppBar(
         leading: const PageBackButton(),
@@ -41,64 +66,62 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
       ),
       body: Column(
         children: [
-          SizedBox(
-            height: 340,
-            child: events.when(
-              data: (events) {
-                return TableCalendar(
-                  locale: EasyLocalization.of(context)!.locale.toString(),
-                  firstDay: DateTime.utc(2010, 10, 16),
-                  lastDay: DateTime.utc(2030, 3, 14),
-                  focusedDay: DateTime.now(),
-                  calendarFormat: CalendarFormat.month,
-                  selectedDayPredicate: (day) {
-                    return isSameDay(selectedDay.value, day);
-                  },
-                  onDaySelected: (value, _) {
-                    selectedDay.value = value;
-                  },
-                  eventLoader: (day) {
-                    return events
-                        .where((e) => isSameDay(e.date, day))
-                        .expand((e) => [...e.statuses, e.checkInResult])
-                        .where((e) => e != null)
-                        .toList();
-                  },
-                  calendarBuilders: CalendarBuilders(
-                    dowBuilder: (context, day) {
-                      final text = DateFormat.EEEEE().format(day);
-                      return Center(child: Text(text));
-                    },
-                    markerBuilder: (context, day, events) {
-                      var checkInResult =
-                          events.whereType<SnCheckInResult>().firstOrNull;
-                      if (checkInResult != null) {
-                        return Positioned(
-                          top: 32,
-                          child: Text(
-                            ['大凶', '凶', '中平', '吉', '大吉'][checkInResult.level],
-                            style: TextStyle(
-                              fontSize: 9,
-                              color:
-                                  isSameDay(selectedDay.value, day)
-                                      ? Theme.of(context).colorScheme.onPrimary
-                                      : Theme.of(context).colorScheme.onSurface,
-                            ),
-                          ),
-                        );
-                      }
-                      return null;
-                    },
-                  ),
-                );
+          TableCalendar(
+            locale: EasyLocalization.of(context)!.locale.toString(),
+            firstDay: DateTime.now().add(Duration(days: -3650)),
+            lastDay: DateTime.now().add(Duration(days: 3650)),
+            focusedDay: DateTime.utc(
+              selectedYear.value,
+              selectedMonth.value,
+              DateTime.now().day,
+            ),
+            calendarFormat: CalendarFormat.month,
+            selectedDayPredicate: (day) {
+              return isSameDay(selectedDay.value, day);
+            },
+            onDaySelected: (value, _) {
+              selectedDay.value = value;
+            },
+            onPageChanged: (focusedDay) {
+              selectedMonth.value = focusedDay.month;
+              selectedYear.value = focusedDay.year;
+            },
+            eventLoader: (day) {
+              return events.value
+                      ?.where((e) => isSameDay(e.date, day))
+                      .expand((e) => [...e.statuses, e.checkInResult])
+                      .where((e) => e != null)
+                      .toList() ??
+                  [];
+            },
+            calendarBuilders: CalendarBuilders(
+              dowBuilder: (context, day) {
+                final text = DateFormat.EEEEE().format(day);
+                return Center(child: Text(text));
+              },
+              markerBuilder: (context, day, events) {
+                var checkInResult =
+                    events.whereType<SnCheckInResult>().firstOrNull;
+                if (checkInResult != null) {
+                  return Positioned(
+                    top: 32,
+                    child: Text(
+                      ['大凶', '凶', '中平', '吉', '大吉'][checkInResult.level],
+                      style: TextStyle(
+                        fontSize: 9,
+                        color:
+                            isSameDay(selectedDay.value, day)
+                                ? Theme.of(context).colorScheme.onPrimary
+                                : Theme.of(context).colorScheme.onSurface,
+                      ),
+                    ),
+                  );
+                }
+                return null;
               },
-              loading: () => const Center(child: CircularProgressIndicator()),
-              error:
-                  (error, stack) =>
-                      Center(child: Text('Error loading calendar: $error')),
             ),
           ),
-          const Divider(height: 1),
+          const Divider(height: 1).padding(top: 8),
           AnimatedSwitcher(
             duration: const Duration(milliseconds: 300),
             child: Builder(
@@ -107,30 +130,27 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
                     events.value
                         ?.where((e) => isSameDay(e.date, selectedDay.value))
                         .firstOrNull;
-                if (event == null) {
-                  return Center(child: Text('eventCalanderEmpty').tr());
-                }
                 return Column(
                   crossAxisAlignment: CrossAxisAlignment.stretch,
                   children: [
-                    Text(DateFormat.EEEE().format(event.date))
+                    Text(DateFormat.EEEE().format(selectedDay.value))
                         .fontSize(16)
                         .bold()
                         .textColor(
                           Theme.of(context).colorScheme.onSecondaryContainer,
                         ),
-                    Text(DateFormat.yMd().format(event.date))
+                    Text(DateFormat.yMd().format(selectedDay.value))
                         .fontSize(12)
                         .textColor(
                           Theme.of(context).colorScheme.onSecondaryContainer,
                         ),
                     const Gap(16),
-                    if (event.checkInResult != null)
+                    if (event?.checkInResult != null)
                       Column(
                         crossAxisAlignment: CrossAxisAlignment.stretch,
                         children: [
                           Text(
-                            'checkInResultLevel${event.checkInResult!.level}',
+                            'checkInResultLevel${event!.checkInResult!.level}',
                           ).tr().fontSize(16).bold(),
                           for (final tip in event.checkInResult!.tips)
                             Row(
@@ -156,7 +176,8 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
                             ).padding(top: 8),
                         ],
                       ),
-                    if (event.checkInResult == null && event.statuses.isEmpty)
+                    if (event?.checkInResult == null &&
+                        (event?.statuses.isEmpty ?? true))
                       Text('eventCalanderEmpty').tr(),
                   ],
                 ).padding(vertical: 24, horizontal: 24);
diff --git a/lib/screens/account/me/event_calendar.freezed.dart b/lib/screens/account/me/event_calendar.freezed.dart
new file mode 100644
index 0000000..023e078
--- /dev/null
+++ b/lib/screens/account/me/event_calendar.freezed.dart
@@ -0,0 +1,148 @@
+// dart format width=80
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'event_calendar.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+// dart format off
+T _$identity<T>(T value) => value;
+/// @nodoc
+mixin _$EventCalendarQuery {
+
+ String? get uname; int get year; int get month;
+/// Create a copy of EventCalendarQuery
+/// with the given fields replaced by the non-null parameter values.
+@JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+$EventCalendarQueryCopyWith<EventCalendarQuery> get copyWith => _$EventCalendarQueryCopyWithImpl<EventCalendarQuery>(this as EventCalendarQuery, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+  return identical(this, other) || (other.runtimeType == runtimeType&&other is EventCalendarQuery&&(identical(other.uname, uname) || other.uname == uname)&&(identical(other.year, year) || other.year == year)&&(identical(other.month, month) || other.month == month));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,uname,year,month);
+
+@override
+String toString() {
+  return 'EventCalendarQuery(uname: $uname, year: $year, month: $month)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class $EventCalendarQueryCopyWith<$Res>  {
+  factory $EventCalendarQueryCopyWith(EventCalendarQuery value, $Res Function(EventCalendarQuery) _then) = _$EventCalendarQueryCopyWithImpl;
+@useResult
+$Res call({
+ String? uname, int year, int month
+});
+
+
+
+
+}
+/// @nodoc
+class _$EventCalendarQueryCopyWithImpl<$Res>
+    implements $EventCalendarQueryCopyWith<$Res> {
+  _$EventCalendarQueryCopyWithImpl(this._self, this._then);
+
+  final EventCalendarQuery _self;
+  final $Res Function(EventCalendarQuery) _then;
+
+/// Create a copy of EventCalendarQuery
+/// with the given fields replaced by the non-null parameter values.
+@pragma('vm:prefer-inline') @override $Res call({Object? uname = freezed,Object? year = null,Object? month = null,}) {
+  return _then(_self.copyWith(
+uname: freezed == uname ? _self.uname : uname // ignore: cast_nullable_to_non_nullable
+as String?,year: null == year ? _self.year : year // ignore: cast_nullable_to_non_nullable
+as int,month: null == month ? _self.month : month // ignore: cast_nullable_to_non_nullable
+as int,
+  ));
+}
+
+}
+
+
+/// @nodoc
+
+
+class _EventCalendarQuery implements EventCalendarQuery {
+  const _EventCalendarQuery({required this.uname, required this.year, required this.month});
+  
+
+@override final  String? uname;
+@override final  int year;
+@override final  int month;
+
+/// Create a copy of EventCalendarQuery
+/// with the given fields replaced by the non-null parameter values.
+@override @JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+_$EventCalendarQueryCopyWith<_EventCalendarQuery> get copyWith => __$EventCalendarQueryCopyWithImpl<_EventCalendarQuery>(this, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+  return identical(this, other) || (other.runtimeType == runtimeType&&other is _EventCalendarQuery&&(identical(other.uname, uname) || other.uname == uname)&&(identical(other.year, year) || other.year == year)&&(identical(other.month, month) || other.month == month));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,uname,year,month);
+
+@override
+String toString() {
+  return 'EventCalendarQuery(uname: $uname, year: $year, month: $month)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class _$EventCalendarQueryCopyWith<$Res> implements $EventCalendarQueryCopyWith<$Res> {
+  factory _$EventCalendarQueryCopyWith(_EventCalendarQuery value, $Res Function(_EventCalendarQuery) _then) = __$EventCalendarQueryCopyWithImpl;
+@override @useResult
+$Res call({
+ String? uname, int year, int month
+});
+
+
+
+
+}
+/// @nodoc
+class __$EventCalendarQueryCopyWithImpl<$Res>
+    implements _$EventCalendarQueryCopyWith<$Res> {
+  __$EventCalendarQueryCopyWithImpl(this._self, this._then);
+
+  final _EventCalendarQuery _self;
+  final $Res Function(_EventCalendarQuery) _then;
+
+/// Create a copy of EventCalendarQuery
+/// with the given fields replaced by the non-null parameter values.
+@override @pragma('vm:prefer-inline') $Res call({Object? uname = freezed,Object? year = null,Object? month = null,}) {
+  return _then(_EventCalendarQuery(
+uname: freezed == uname ? _self.uname : uname // ignore: cast_nullable_to_non_nullable
+as String?,year: null == year ? _self.year : year // ignore: cast_nullable_to_non_nullable
+as int,month: null == month ? _self.month : month // ignore: cast_nullable_to_non_nullable
+as int,
+  ));
+}
+
+
+}
+
+// dart format on
diff --git a/lib/screens/account/me/event_calendar.g.dart b/lib/screens/account/me/event_calendar.g.dart
index 489809e..01d8726 100644
--- a/lib/screens/account/me/event_calendar.g.dart
+++ b/lib/screens/account/me/event_calendar.g.dart
@@ -6,26 +6,155 @@ part of 'event_calendar.dart';
 // RiverpodGenerator
 // **************************************************************************
 
-String _$myselfAccountEventCalendarHash() =>
-    r'd3faad342d5104e87ff6597ab459e65cb5f4ef97';
+String _$accountEventCalendarHash() =>
+    r'57405caaf53a83d121b6bb4b70540134fb581525';
 
-/// See also [myselfAccountEventCalendar].
-@ProviderFor(myselfAccountEventCalendar)
-final myselfAccountEventCalendarProvider =
-    AutoDisposeFutureProvider<List<SnEventCalendarEntry>>.internal(
-      myselfAccountEventCalendar,
-      name: r'myselfAccountEventCalendarProvider',
-      debugGetCreateSourceHash:
-          const bool.fromEnvironment('dart.vm.product')
-              ? null
-              : _$myselfAccountEventCalendarHash,
-      dependencies: null,
-      allTransitiveDependencies: null,
+/// Copied from Dart SDK
+class _SystemHash {
+  _SystemHash._();
+
+  static int combine(int hash, int value) {
+    // ignore: parameter_assignments
+    hash = 0x1fffffff & (hash + value);
+    // ignore: parameter_assignments
+    hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
+    return hash ^ (hash >> 6);
+  }
+
+  static int finish(int hash) {
+    // ignore: parameter_assignments
+    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
+    // ignore: parameter_assignments
+    hash = hash ^ (hash >> 11);
+    return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
+  }
+}
+
+/// See also [accountEventCalendar].
+@ProviderFor(accountEventCalendar)
+const accountEventCalendarProvider = AccountEventCalendarFamily();
+
+/// See also [accountEventCalendar].
+class AccountEventCalendarFamily
+    extends Family<AsyncValue<List<SnEventCalendarEntry>>> {
+  /// See also [accountEventCalendar].
+  const AccountEventCalendarFamily();
+
+  /// See also [accountEventCalendar].
+  AccountEventCalendarProvider call(EventCalendarQuery query) {
+    return AccountEventCalendarProvider(query);
+  }
+
+  @override
+  AccountEventCalendarProvider getProviderOverride(
+    covariant AccountEventCalendarProvider provider,
+  ) {
+    return call(provider.query);
+  }
+
+  static const Iterable<ProviderOrFamily>? _dependencies = null;
+
+  @override
+  Iterable<ProviderOrFamily>? get dependencies => _dependencies;
+
+  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
+
+  @override
+  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
+      _allTransitiveDependencies;
+
+  @override
+  String? get name => r'accountEventCalendarProvider';
+}
+
+/// See also [accountEventCalendar].
+class AccountEventCalendarProvider
+    extends AutoDisposeFutureProvider<List<SnEventCalendarEntry>> {
+  /// See also [accountEventCalendar].
+  AccountEventCalendarProvider(EventCalendarQuery query)
+    : this._internal(
+        (ref) => accountEventCalendar(ref as AccountEventCalendarRef, query),
+        from: accountEventCalendarProvider,
+        name: r'accountEventCalendarProvider',
+        debugGetCreateSourceHash:
+            const bool.fromEnvironment('dart.vm.product')
+                ? null
+                : _$accountEventCalendarHash,
+        dependencies: AccountEventCalendarFamily._dependencies,
+        allTransitiveDependencies:
+            AccountEventCalendarFamily._allTransitiveDependencies,
+        query: query,
+      );
+
+  AccountEventCalendarProvider._internal(
+    super._createNotifier, {
+    required super.name,
+    required super.dependencies,
+    required super.allTransitiveDependencies,
+    required super.debugGetCreateSourceHash,
+    required super.from,
+    required this.query,
+  }) : super.internal();
+
+  final EventCalendarQuery query;
+
+  @override
+  Override overrideWith(
+    FutureOr<List<SnEventCalendarEntry>> Function(
+      AccountEventCalendarRef provider,
+    )
+    create,
+  ) {
+    return ProviderOverride(
+      origin: this,
+      override: AccountEventCalendarProvider._internal(
+        (ref) => create(ref as AccountEventCalendarRef),
+        from: from,
+        name: null,
+        dependencies: null,
+        allTransitiveDependencies: null,
+        debugGetCreateSourceHash: null,
+        query: query,
+      ),
     );
+  }
+
+  @override
+  AutoDisposeFutureProviderElement<List<SnEventCalendarEntry>> createElement() {
+    return _AccountEventCalendarProviderElement(this);
+  }
+
+  @override
+  bool operator ==(Object other) {
+    return other is AccountEventCalendarProvider && other.query == query;
+  }
+
+  @override
+  int get hashCode {
+    var hash = _SystemHash.combine(0, runtimeType.hashCode);
+    hash = _SystemHash.combine(hash, query.hashCode);
+
+    return _SystemHash.finish(hash);
+  }
+}
 
 @Deprecated('Will be removed in 3.0. Use Ref instead')
 // ignore: unused_element
-typedef MyselfAccountEventCalendarRef =
-    AutoDisposeFutureProviderRef<List<SnEventCalendarEntry>>;
+mixin AccountEventCalendarRef
+    on AutoDisposeFutureProviderRef<List<SnEventCalendarEntry>> {
+  /// The parameter `query` of this provider.
+  EventCalendarQuery get query;
+}
+
+class _AccountEventCalendarProviderElement
+    extends AutoDisposeFutureProviderElement<List<SnEventCalendarEntry>>
+    with AccountEventCalendarRef {
+  _AccountEventCalendarProviderElement(super.provider);
+
+  @override
+  EventCalendarQuery get query =>
+      (origin as AccountEventCalendarProvider).query;
+}
+
 // ignore_for_file: type=lint
 // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package