✨ Notable day countdown
This commit is contained in:
@@ -976,5 +976,6 @@
|
|||||||
"shuffle": "Shuffle",
|
"shuffle": "Shuffle",
|
||||||
"pinned": "Pinned",
|
"pinned": "Pinned",
|
||||||
"noResultsFound": "No results found",
|
"noResultsFound": "No results found",
|
||||||
"toggleFilters": "Toggle filters"
|
"toggleFilters": "Toggle filters",
|
||||||
|
"notableDayNext": "{} is in"
|
||||||
}
|
}
|
||||||
|
@@ -863,5 +863,6 @@
|
|||||||
"statusPresent": "至今",
|
"statusPresent": "至今",
|
||||||
"accountAutomated": "机器人",
|
"accountAutomated": "机器人",
|
||||||
"openInBrowser": "在浏览器中打开",
|
"openInBrowser": "在浏览器中打开",
|
||||||
"highlightPost": "精选帖子"
|
"highlightPost": "精选帖子",
|
||||||
|
"notableDayNext": "距离 {} 还有"
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ sealed class SnAccount with _$SnAccount {
|
|||||||
required String name,
|
required String name,
|
||||||
required String nick,
|
required String nick,
|
||||||
required String language,
|
required String language,
|
||||||
required String region,
|
@Default("") String region,
|
||||||
required bool isSuperuser,
|
required bool isSuperuser,
|
||||||
required String? automatedId,
|
required String? automatedId,
|
||||||
required SnAccountProfile profile,
|
required SnAccountProfile profile,
|
||||||
|
@@ -236,14 +236,14 @@ return $default(_that.id,_that.name,_that.nick,_that.language,_that.region,_that
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnAccount implements SnAccount {
|
class _SnAccount implements SnAccount {
|
||||||
const _SnAccount({required this.id, required this.name, required this.nick, required this.language, required this.region, required this.isSuperuser, required this.automatedId, required this.profile, required this.perkSubscription, final List<SnAccountBadge> badges = const [], required this.createdAt, required this.updatedAt, required this.deletedAt}): _badges = badges;
|
const _SnAccount({required this.id, required this.name, required this.nick, required this.language, this.region = "", required this.isSuperuser, required this.automatedId, required this.profile, required this.perkSubscription, final List<SnAccountBadge> badges = const [], required this.createdAt, required this.updatedAt, required this.deletedAt}): _badges = badges;
|
||||||
factory _SnAccount.fromJson(Map<String, dynamic> json) => _$SnAccountFromJson(json);
|
factory _SnAccount.fromJson(Map<String, dynamic> json) => _$SnAccountFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@override final String name;
|
@override final String name;
|
||||||
@override final String nick;
|
@override final String nick;
|
||||||
@override final String language;
|
@override final String language;
|
||||||
@override final String region;
|
@override@JsonKey() final String region;
|
||||||
@override final bool isSuperuser;
|
@override final bool isSuperuser;
|
||||||
@override final String? automatedId;
|
@override final String? automatedId;
|
||||||
@override final SnAccountProfile profile;
|
@override final SnAccountProfile profile;
|
||||||
|
@@ -11,7 +11,7 @@ _SnAccount _$SnAccountFromJson(Map<String, dynamic> json) => _SnAccount(
|
|||||||
name: json['name'] as String,
|
name: json['name'] as String,
|
||||||
nick: json['nick'] as String,
|
nick: json['nick'] as String,
|
||||||
language: json['language'] as String,
|
language: json['language'] as String,
|
||||||
region: json['region'] as String,
|
region: json['region'] as String? ?? "",
|
||||||
isSuperuser: json['is_superuser'] as bool,
|
isSuperuser: json['is_superuser'] as bool,
|
||||||
automatedId: json['automated_id'] as String?,
|
automatedId: json['automated_id'] as String?,
|
||||||
profile: SnAccountProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
profile: SnAccountProfile.fromJson(json['profile'] as Map<String, dynamic>),
|
||||||
|
@@ -4,6 +4,20 @@ import 'package:island/models/account.dart';
|
|||||||
part 'activity.freezed.dart';
|
part 'activity.freezed.dart';
|
||||||
part 'activity.g.dart';
|
part 'activity.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnNotableDay with _$SnNotableDay {
|
||||||
|
const factory SnNotableDay({
|
||||||
|
required DateTime date,
|
||||||
|
required String localName,
|
||||||
|
required String globalName,
|
||||||
|
required String countryCode,
|
||||||
|
required List<int> holidays,
|
||||||
|
}) = _SnNotableDay;
|
||||||
|
|
||||||
|
factory SnNotableDay.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnNotableDayFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class SnActivity with _$SnActivity {
|
sealed class SnActivity with _$SnActivity {
|
||||||
const factory SnActivity({
|
const factory SnActivity({
|
||||||
|
@@ -12,6 +12,281 @@ part of 'activity.dart';
|
|||||||
// dart format off
|
// dart format off
|
||||||
T _$identity<T>(T value) => value;
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnNotableDay {
|
||||||
|
|
||||||
|
DateTime get date; String get localName; String get globalName; String get countryCode; List<int> get holidays;
|
||||||
|
/// Create a copy of SnNotableDay
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnNotableDayCopyWith<SnNotableDay> get copyWith => _$SnNotableDayCopyWithImpl<SnNotableDay>(this as SnNotableDay, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnNotableDay to a JSON map.
|
||||||
|
Map<String, dynamic> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,date,localName,globalName,countryCode,const DeepCollectionEquality().hash(holidays));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnNotableDay(date: $date, localName: $localName, globalName: $globalName, countryCode: $countryCode, holidays: $holidays)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
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<int> holidays
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnNotableDayCopyWithImpl<$Res>
|
||||||
|
implements $SnNotableDayCopyWith<$Res> {
|
||||||
|
_$SnNotableDayCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnNotableDay _self;
|
||||||
|
final $Res Function(SnNotableDay) _then;
|
||||||
|
|
||||||
|
/// 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,}) {
|
||||||
|
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 List<int>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnNotableDay].
|
||||||
|
extension SnNotableDayPatterns on SnNotableDay {
|
||||||
|
/// 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( _SnNotableDay value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnNotableDay() 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( _SnNotableDay value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnNotableDay():
|
||||||
|
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( _SnNotableDay value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnNotableDay() 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( DateTime date, String localName, String globalName, String countryCode, List<int> 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 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( DateTime date, String localName, String globalName, String countryCode, List<int> holidays) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnNotableDay():
|
||||||
|
return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_that.holidays);}
|
||||||
|
}
|
||||||
|
/// 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( DateTime date, String localName, String globalName, String countryCode, List<int> 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 null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnNotableDay implements SnNotableDay {
|
||||||
|
const _SnNotableDay({required this.date, required this.localName, required this.globalName, required this.countryCode, required final List<int> holidays}): _holidays = holidays;
|
||||||
|
factory _SnNotableDay.fromJson(Map<String, dynamic> json) => _$SnNotableDayFromJson(json);
|
||||||
|
|
||||||
|
@override final DateTime date;
|
||||||
|
@override final String localName;
|
||||||
|
@override final String globalName;
|
||||||
|
@override final String countryCode;
|
||||||
|
final List<int> _holidays;
|
||||||
|
@override List<int> get holidays {
|
||||||
|
if (_holidays is EqualUnmodifiableListView) return _holidays;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_holidays);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Create a copy of SnNotableDay
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnNotableDayCopyWith<_SnNotableDay> get copyWith => __$SnNotableDayCopyWithImpl<_SnNotableDay>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnNotableDayToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,date,localName,globalName,countryCode,const DeepCollectionEquality().hash(_holidays));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnNotableDay(date: $date, localName: $localName, globalName: $globalName, countryCode: $countryCode, holidays: $holidays)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnNotableDayCopyWith<$Res> implements $SnNotableDayCopyWith<$Res> {
|
||||||
|
factory _$SnNotableDayCopyWith(_SnNotableDay value, $Res Function(_SnNotableDay) _then) = __$SnNotableDayCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
DateTime date, String localName, String globalName, String countryCode, List<int> holidays
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnNotableDayCopyWithImpl<$Res>
|
||||||
|
implements _$SnNotableDayCopyWith<$Res> {
|
||||||
|
__$SnNotableDayCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnNotableDay _self;
|
||||||
|
final $Res Function(_SnNotableDay) _then;
|
||||||
|
|
||||||
|
/// 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,}) {
|
||||||
|
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 List<int>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnActivity {
|
mixin _$SnActivity {
|
||||||
|
|
||||||
|
@@ -6,6 +6,27 @@ part of 'activity.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
|
_SnNotableDay _$SnNotableDayFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnNotableDay(
|
||||||
|
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,
|
||||||
|
holidays:
|
||||||
|
(json['holidays'] as List<dynamic>)
|
||||||
|
.map((e) => (e as num).toInt())
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnNotableDayToJson(_SnNotableDay instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'date': instance.date.toIso8601String(),
|
||||||
|
'local_name': instance.localName,
|
||||||
|
'global_name': instance.globalName,
|
||||||
|
'country_code': instance.countryCode,
|
||||||
|
'holidays': instance.holidays,
|
||||||
|
};
|
||||||
|
|
||||||
_SnActivity _$SnActivityFromJson(Map<String, dynamic> json) => _SnActivity(
|
_SnActivity _$SnActivityFromJson(Map<String, dynamic> json) => _SnActivity(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
type: json['type'] as String,
|
type: json['type'] as String,
|
||||||
|
@@ -365,9 +365,7 @@ class PostActionButtons extends HookConsumerWidget {
|
|||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(horizontal: renderingPadding.horizontal),
|
||||||
horizontal: renderingPadding.horizontal + 4,
|
|
||||||
),
|
|
||||||
children: children,
|
children: children,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@@ -46,6 +46,10 @@ class EventDetailsWidget extends StatelessWidget {
|
|||||||
size: 12,
|
size: 12,
|
||||||
fill: 1,
|
fill: 1,
|
||||||
).padding(top: 4, right: 4),
|
).padding(top: 4, right: 4),
|
||||||
|
Icon(
|
||||||
|
tip.isPositive ? Symbols.thumb_up : Symbols.thumb_down,
|
||||||
|
size: 14,
|
||||||
|
).padding(top: 2.5),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@@ -14,6 +15,7 @@ import 'package:island/widgets/alert.dart';
|
|||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:slide_countdown/slide_countdown.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
part 'check_in.g.dart';
|
part 'check_in.g.dart';
|
||||||
@@ -34,6 +36,17 @@ Future<SnCheckInResult?> checkInResultToday(Ref ref) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnNotableDay?> nextNotableDay(Ref ref) async {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
try {
|
||||||
|
final resp = await client.get('/id/notable/me/next');
|
||||||
|
return SnNotableDay.fromJson(resp.data);
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CheckInWidget extends HookConsumerWidget {
|
class CheckInWidget extends HookConsumerWidget {
|
||||||
final EdgeInsets? margin;
|
final EdgeInsets? margin;
|
||||||
final VoidCallback? onChecked;
|
final VoidCallback? onChecked;
|
||||||
@@ -42,6 +55,22 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final todayResult = ref.watch(checkInResultTodayProvider);
|
final todayResult = ref.watch(checkInResultTodayProvider);
|
||||||
|
final nextNotableDay = ref.watch(nextNotableDayProvider);
|
||||||
|
|
||||||
|
final userinfo = ref.watch(userInfoProvider);
|
||||||
|
final isAdult = useMemoized(() {
|
||||||
|
final birthday = userinfo.value?.profile.birthday;
|
||||||
|
if (birthday == null) return false;
|
||||||
|
final now = DateTime.now();
|
||||||
|
final age =
|
||||||
|
now.year -
|
||||||
|
birthday.year -
|
||||||
|
((now.month < birthday.month ||
|
||||||
|
(now.month == birthday.month && now.day < birthday.day))
|
||||||
|
? 1
|
||||||
|
: 0);
|
||||||
|
return age >= 18;
|
||||||
|
}, [userinfo]);
|
||||||
|
|
||||||
Future<void> checkIn({String? captchatTk}) async {
|
Future<void> checkIn({String? captchatTk}) async {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
@@ -71,119 +100,147 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
return Card(
|
return Card(
|
||||||
margin:
|
margin:
|
||||||
margin ?? EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
margin ?? EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 8),
|
||||||
child: Row(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
spacing: 16,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
Column(
|
||||||
borderRadius: BorderRadius.circular(8),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Container(
|
children: [
|
||||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
Row(
|
||||||
width: 56,
|
spacing: 8,
|
||||||
height: 56,
|
mainAxisSize: MainAxisSize.min,
|
||||||
child:
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
Column(
|
children: [
|
||||||
mainAxisSize: MainAxisSize.min,
|
Icon(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
switch (DateTime.now().weekday) {
|
||||||
children: [
|
6 || 7 => Symbols.weekend,
|
||||||
Text(DateFormat('EEE').format(DateTime.now()))
|
_ => isAdult ? Symbols.work : Symbols.school,
|
||||||
.fontSize(16)
|
},
|
||||||
.bold()
|
fill: 1,
|
||||||
.textColor(
|
size: 16,
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
).padding(right: 2),
|
||||||
),
|
Text(DateFormat('EEE').format(DateTime.now()))
|
||||||
Text(DateFormat('MM/dd').format(DateTime.now()))
|
.fontSize(16)
|
||||||
.fontSize(12)
|
.bold()
|
||||||
.textColor(
|
.textColor(
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
),
|
|
||||||
],
|
|
||||||
).center(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: todayResult.when(
|
|
||||||
data: (result) {
|
|
||||||
if (result == null) return _CheckInNoneWidget();
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'checkInResultLevel${result.level}',
|
|
||||||
).tr().fontSize(15).bold(),
|
|
||||||
Wrap(
|
|
||||||
children:
|
|
||||||
result.tips
|
|
||||||
.map((e) {
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
e.isPositive
|
|
||||||
? Symbols.thumb_up
|
|
||||||
: Symbols.thumb_down,
|
|
||||||
size: 12,
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
Text(e.title).fontSize(11),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.toList()
|
|
||||||
.expand(
|
|
||||||
(widget) => [
|
|
||||||
widget,
|
|
||||||
Text(' · ').fontSize(11),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.toList()
|
|
||||||
..removeLast(),
|
|
||||||
),
|
),
|
||||||
],
|
Text(DateFormat('MM/dd').format(DateTime.now()))
|
||||||
);
|
.fontSize(12)
|
||||||
|
.textColor(
|
||||||
|
Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
|
)
|
||||||
|
.padding(top: 2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
spacing: 5,
|
||||||
|
children: [
|
||||||
|
Text('notableDayNext')
|
||||||
|
.tr(args: [nextNotableDay.value?.localName ?? 'idk'])
|
||||||
|
.fontSize(12),
|
||||||
|
SlideCountdown(
|
||||||
|
decoration: const BoxDecoration(),
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
separatorStyle: const TextStyle(fontSize: 12),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
duration: nextNotableDay.value?.date.difference(
|
||||||
|
DateTime.now(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 16, top: 8),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: todayResult.when(
|
||||||
|
data: (result) {
|
||||||
|
if (result == null) return _CheckInNoneWidget();
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'checkInResultLevel${result.level}',
|
||||||
|
).tr().fontSize(15).bold(),
|
||||||
|
Wrap(
|
||||||
|
children:
|
||||||
|
result.tips
|
||||||
|
.map((e) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
e.isPositive
|
||||||
|
? Symbols.thumb_up
|
||||||
|
: Symbols.thumb_down,
|
||||||
|
size: 12,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(e.title).fontSize(11),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.toList()
|
||||||
|
.expand(
|
||||||
|
(widget) => [
|
||||||
|
widget,
|
||||||
|
Text(' · ').fontSize(11),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.toList()
|
||||||
|
..removeLast(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => _CheckInNoneWidget(),
|
||||||
|
error:
|
||||||
|
(err, stack) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('error').tr().fontSize(15).bold(),
|
||||||
|
Text(err.toString()).fontSize(11),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton.outlined(
|
||||||
|
onPressed: () {
|
||||||
|
if (todayResult.valueOrNull == null) {
|
||||||
|
checkIn();
|
||||||
|
} else {
|
||||||
|
context.pushNamed(
|
||||||
|
'accountCalendar',
|
||||||
|
pathParameters: {'name': 'me'},
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
loading: () => _CheckInNoneWidget(),
|
icon: AnimatedSwitcher(
|
||||||
error:
|
duration: const Duration(milliseconds: 300),
|
||||||
(err, stack) => Column(
|
child: todayResult.when(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
data:
|
||||||
children: [
|
(result) => Icon(
|
||||||
Text('error').tr().fontSize(15).bold(),
|
result == null
|
||||||
Text(err.toString()).fontSize(11),
|
? Symbols.local_fire_department
|
||||||
],
|
: Symbols.event,
|
||||||
),
|
key: ValueKey(result != null),
|
||||||
|
),
|
||||||
|
loading: () => const Icon(Symbols.refresh),
|
||||||
|
error: (_, _) => const Icon(Symbols.error),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
).padding(horizontal: 16, bottom: 12, top: 4),
|
||||||
IconButton.outlined(
|
|
||||||
onPressed: () {
|
|
||||||
if (todayResult.valueOrNull == null) {
|
|
||||||
checkIn();
|
|
||||||
} else {
|
|
||||||
context.pushNamed(
|
|
||||||
'accountCalendar',
|
|
||||||
pathParameters: {'name': 'me'},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: todayResult.when(
|
|
||||||
data:
|
|
||||||
(result) => Icon(
|
|
||||||
result == null
|
|
||||||
? Symbols.local_fire_department
|
|
||||||
: Symbols.event,
|
|
||||||
key: ValueKey(result != null),
|
|
||||||
),
|
|
||||||
loading: () => const Icon(Symbols.refresh),
|
|
||||||
error: (_, _) => const Icon(Symbols.error),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,5 +26,24 @@ final checkInResultTodayProvider =
|
|||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef CheckInResultTodayRef = AutoDisposeFutureProviderRef<SnCheckInResult?>;
|
typedef CheckInResultTodayRef = AutoDisposeFutureProviderRef<SnCheckInResult?>;
|
||||||
|
String _$nextNotableDayHash() => r'698370bec4be28774d332412c5a701f914064c90';
|
||||||
|
|
||||||
|
/// See also [nextNotableDay].
|
||||||
|
@ProviderFor(nextNotableDay)
|
||||||
|
final nextNotableDayProvider =
|
||||||
|
AutoDisposeFutureProvider<SnNotableDay?>.internal(
|
||||||
|
nextNotableDay,
|
||||||
|
name: r'nextNotableDayProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$nextNotableDayHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
typedef NextNotableDayRef = AutoDisposeFutureProviderRef<SnNotableDay?>;
|
||||||
// ignore_for_file: type=lint
|
// 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
|
// 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
|
||||||
|
@@ -321,7 +321,7 @@ class AttachmentPreview extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Icon(fallbackIcon),
|
Icon(fallbackIcon),
|
||||||
const Gap(6),
|
const Gap(6),
|
||||||
Text(file.name),
|
Text(file.name, textAlign: TextAlign.center),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: file.length(),
|
future: file.length(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
|
16
pubspec.lock
16
pubspec.lock
@@ -1789,6 +1789,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
pausable_timer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pausable_timer
|
||||||
|
sha256: "6ef1a95441ec3439de6fb63f39a011b67e693198e7dae14e20675c3c00e86074"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0+3"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -2258,6 +2266,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
slide_countdown:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: slide_countdown
|
||||||
|
sha256: "363914f96389502467d4dc9c0f26e88f93df3d8e37de2d5ff05b16d981fe973d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
source_gen:
|
source_gen:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -144,6 +144,7 @@ dependencies:
|
|||||||
flutter_webrtc: ^1.1.0
|
flutter_webrtc: ^1.1.0
|
||||||
flutter_local_notifications: ^19.4.1
|
flutter_local_notifications: ^19.4.1
|
||||||
wakelock_plus: ^1.3.2
|
wakelock_plus: ^1.3.2
|
||||||
|
slide_countdown: ^2.0.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user