Compare commits

..

14 Commits

28 changed files with 1321 additions and 476 deletions

View File

@@ -143,6 +143,7 @@
"connectionConnected": "Connected", "connectionConnected": "Connected",
"connectionDisconnected": "Disconnected", "connectionDisconnected": "Disconnected",
"connectionReconnecting": "Reconnecting", "connectionReconnecting": "Reconnecting",
"connectionServerDown": "Unable to Connect",
"accountConnections": "Account Connections", "accountConnections": "Account Connections",
"accountConnectionsDescription": "Manage your external account connections", "accountConnectionsDescription": "Manage your external account connections",
"accountConnectionAdd": "Add Connection", "accountConnectionAdd": "Add Connection",
@@ -1020,6 +1021,7 @@
"noResultsFound": "No results found", "noResultsFound": "No results found",
"toggleFilters": "Toggle filters", "toggleFilters": "Toggle filters",
"notableDayNext": "{} is in", "notableDayNext": "{} is in",
"notableDayToday": "{} is today!",
"expandPoll": "Expand Poll", "expandPoll": "Expand Poll",
"collapsePoll": "Collapse Poll", "collapsePoll": "Collapse Poll",
"embedView": "Embed View", "embedView": "Embed View",
@@ -1537,5 +1539,8 @@
"settingsGroupedChatList": "Grouped Chat List", "settingsGroupedChatList": "Grouped Chat List",
"settingsNotifyWithHaptic": "Notification with Haptic Feedback", "settingsNotifyWithHaptic": "Notification with Haptic Feedback",
"settingsDashSearchEngine": "Search Engine for web", "settingsDashSearchEngine": "Search Engine for web",
"settingsDashSearchEngineHelper": "Use %s as the placeholder for the query." "settingsDashSearchEngineHelper": "Use %s as the placeholder for the query.",
"settingsDefaultScreen": "Default Screen",
"notableDayChristmas": "Christmas",
"notableDayNewYear": "New Year"
} }

View File

@@ -999,6 +999,7 @@
"noResultsFound": "未找到结果", "noResultsFound": "未找到结果",
"toggleFilters": "切换过滤器", "toggleFilters": "切换过滤器",
"notableDayNext": "距离 {} 还有", "notableDayNext": "距离 {} 还有",
"notableDayToday": "今天是 {}!",
"expandPoll": "展开投票", "expandPoll": "展开投票",
"collapsePoll": "折叠投票", "collapsePoll": "折叠投票",
"embedView": "嵌入视图", "embedView": "嵌入视图",

View File

@@ -10,7 +10,8 @@ sealed class SnNotableDay with _$SnNotableDay {
required DateTime date, required DateTime date,
required String localName, required String localName,
required String globalName, required String globalName,
required String countryCode, required String? countryCode,
required String? localizableKey,
required List<int> holidays, required List<int> holidays,
}) = _SnNotableDay; }) = _SnNotableDay;

View File

@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$SnNotableDay { mixin _$SnNotableDay {
DateTime get date; String get localName; String get globalName; String get countryCode; List<int> get holidays; DateTime get date; String get localName; String get globalName; String? get countryCode; String? get localizableKey; List<int> get holidays;
/// Create a copy of SnNotableDay /// Create a copy of SnNotableDay
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -28,16 +28,16 @@ $SnNotableDayCopyWith<SnNotableDay> get copyWith => _$SnNotableDayCopyWithImpl<S
@override @override
bool operator ==(Object other) { 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) @JsonKey(includeFromJson: false, includeToJson: false)
@override @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 @override
String toString() { 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; factory $SnNotableDayCopyWith(SnNotableDay value, $Res Function(SnNotableDay) _then) = _$SnNotableDayCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
DateTime date, String localName, String globalName, String countryCode, List<int> holidays DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List<int> holidays
}); });
@@ -65,13 +65,14 @@ class _$SnNotableDayCopyWithImpl<$Res>
/// Create a copy of SnNotableDay /// Create a copy of SnNotableDay
/// with the given fields replaced by the non-null parameter values. /// 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( return _then(_self.copyWith(
date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable 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 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,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,countryCode: freezed == 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?,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<int>, as List<int>,
)); ));
} }
@@ -154,10 +155,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@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; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List<int> holidays)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnNotableDay() when $default != null: 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(); return orElse();
} }
@@ -175,10 +176,10 @@ return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_t
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime date, String localName, String globalName, String countryCode, List<int> holidays) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List<int> holidays) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnNotableDay(): 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` /// 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 extends Object?>(TResult? Function( DateTime date, String localName, String globalName, String countryCode, List<int> holidays)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List<int> holidays)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnNotableDay() when $default != null: 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; return null;
} }
@@ -207,13 +208,14 @@ return $default(_that.date,_that.localName,_that.globalName,_that.countryCode,_t
@JsonSerializable() @JsonSerializable()
class _SnNotableDay implements SnNotableDay { 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; const _SnNotableDay({required this.date, required this.localName, required this.globalName, required this.countryCode, required this.localizableKey, required final List<int> holidays}): _holidays = holidays;
factory _SnNotableDay.fromJson(Map<String, dynamic> json) => _$SnNotableDayFromJson(json); factory _SnNotableDay.fromJson(Map<String, dynamic> json) => _$SnNotableDayFromJson(json);
@override final DateTime date; @override final DateTime date;
@override final String localName; @override final String localName;
@override final String globalName; @override final String globalName;
@override final String countryCode; @override final String? countryCode;
@override final String? localizableKey;
final List<int> _holidays; final List<int> _holidays;
@override List<int> get holidays { @override List<int> get holidays {
if (_holidays is EqualUnmodifiableListView) return _holidays; if (_holidays is EqualUnmodifiableListView) return _holidays;
@@ -235,16 +237,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { 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) @JsonKey(includeFromJson: false, includeToJson: false)
@override @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 @override
String toString() { 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; factory _$SnNotableDayCopyWith(_SnNotableDay value, $Res Function(_SnNotableDay) _then) = __$SnNotableDayCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
DateTime date, String localName, String globalName, String countryCode, List<int> holidays DateTime date, String localName, String globalName, String? countryCode, String? localizableKey, List<int> holidays
}); });
@@ -272,13 +274,14 @@ class __$SnNotableDayCopyWithImpl<$Res>
/// Create a copy of SnNotableDay /// Create a copy of SnNotableDay
/// with the given fields replaced by the non-null parameter values. /// 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( return _then(_SnNotableDay(
date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable 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 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,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,countryCode: freezed == 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?,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<int>, as List<int>,
)); ));
} }

View File

@@ -11,7 +11,8 @@ _SnNotableDay _$SnNotableDayFromJson(Map<String, dynamic> json) =>
date: DateTime.parse(json['date'] as String), date: DateTime.parse(json['date'] as String),
localName: json['local_name'] as String, localName: json['local_name'] as String,
globalName: json['global_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<dynamic>) holidays: (json['holidays'] as List<dynamic>)
.map((e) => (e as num).toInt()) .map((e) => (e as num).toInt())
.toList(), .toList(),
@@ -23,6 +24,7 @@ Map<String, dynamic> _$SnNotableDayToJson(_SnNotableDay instance) =>
'local_name': instance.localName, 'local_name': instance.localName,
'global_name': instance.globalName, 'global_name': instance.globalName,
'country_code': instance.countryCode, 'country_code': instance.countryCode,
'localizable_key': instance.localizableKey,
'holidays': instance.holidays, 'holidays': instance.holidays,
}; };

16
lib/models/fortune.dart Normal file
View File

@@ -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<String, dynamic> json) =>
_$SnFortuneSayingFromJson(json);
}

View File

@@ -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>(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<SnFortuneSaying> get copyWith => _$SnFortuneSayingCopyWithImpl<SnFortuneSaying>(this as SnFortuneSaying, _$identity);
/// Serializes this SnFortuneSaying to a JSON map.
Map<String, dynamic> 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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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 extends Object?>(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<String, dynamic> 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<String, dynamic> 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

21
lib/models/fortune.g.dart Normal file
View File

@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'fortune.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SnFortuneSaying _$SnFortuneSayingFromJson(Map<String, dynamic> json) =>
_SnFortuneSaying(
content: json['content'] as String,
source: json['source'] as String,
language: json['language'] as String,
);
Map<String, dynamic> _$SnFortuneSayingToJson(_SnFortuneSaying instance) =>
<String, dynamic>{
'content': instance.content,
'source': instance.source,
'language': instance.language,
};

View File

@@ -32,7 +32,6 @@ const kAppEnterToSend = 'app_enter_to_send';
const kAppDefaultPoolId = 'app_default_pool_id'; const kAppDefaultPoolId = 'app_default_pool_id';
const kAppMessageDisplayStyle = 'app_message_display_style'; const kAppMessageDisplayStyle = 'app_message_display_style';
const kAppThemeMode = 'app_theme_mode'; const kAppThemeMode = 'app_theme_mode';
const kMaterialYouToggleStoreKey = 'app_theme_material_you';
const kAppDisableAnimation = 'app_disable_animation'; const kAppDisableAnimation = 'app_disable_animation';
const kAppFabPosition = 'app_fab_position'; const kAppFabPosition = 'app_fab_position';
const kAppGroupedChatList = 'app_grouped_chat_list'; const kAppGroupedChatList = 'app_grouped_chat_list';
@@ -41,26 +40,13 @@ const kFeaturedPostsCollapsedId =
const kAppFirstLaunchAt = 'app_first_launch_at'; const kAppFirstLaunchAt = 'app_first_launch_at';
const kAppAskedReview = 'app_asked_review'; const kAppAskedReview = 'app_asked_review';
const kAppDashSearchEngine = 'app_dash_search_engine'; const kAppDashSearchEngine = 'app_dash_search_engine';
const kAppDefaultScreen = 'app_default_screen';
const Map<String, FilterQuality> kImageQualityLevel = { // Will be overrided by the ProviderScope
'settingsImageQualityLowest': FilterQuality.none,
'settingsImageQualityLow': FilterQuality.low,
'settingsImageQualityMedium': FilterQuality.medium,
'settingsImageQualityHigh': FilterQuality.high,
};
final sharedPreferencesProvider = Provider<SharedPreferences>((ref) { final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
throw UnimplementedError(); throw UnimplementedError();
}); });
final imageQualityProvider = Provider<FilterQuality>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return kImageQualityLevel.values.elementAtOrNull(
prefs.getInt('app_image_quality') ?? 3,
) ??
FilterQuality.high;
});
final serverUrlProvider = Provider<String>((ref) { final serverUrlProvider = Provider<String>((ref) {
final prefs = ref.watch(sharedPreferencesProvider); final prefs = ref.watch(sharedPreferencesProvider);
return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault; return prefs.getString(kNetworkServerStoreKey) ?? kNetworkServerDefault;
@@ -100,13 +86,13 @@ sealed class AppSettings with _$AppSettings {
required String? defaultPoolId, required String? defaultPoolId,
required String messageDisplayStyle, required String messageDisplayStyle,
required String? themeMode, required String? themeMode,
required bool useMaterial3,
required bool disableAnimation, required bool disableAnimation,
required String fabPosition, required String fabPosition,
required bool groupedChatList, required bool groupedChatList,
required String? firstLaunchAt, required String? firstLaunchAt,
required bool askedReview, required bool askedReview,
required String? dashSearchEngine, required String? dashSearchEngine,
required String? defaultScreen,
}) = _AppSettings; }) = _AppSettings;
} }
@@ -132,13 +118,13 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
defaultPoolId: prefs.getString(kAppDefaultPoolId), defaultPoolId: prefs.getString(kAppDefaultPoolId),
messageDisplayStyle: prefs.getString(kAppMessageDisplayStyle) ?? 'bubble', messageDisplayStyle: prefs.getString(kAppMessageDisplayStyle) ?? 'bubble',
themeMode: prefs.getString(kAppThemeMode) ?? 'system', themeMode: prefs.getString(kAppThemeMode) ?? 'system',
useMaterial3: prefs.getBool(kMaterialYouToggleStoreKey) ?? true,
disableAnimation: prefs.getBool(kAppDisableAnimation) ?? false, disableAnimation: prefs.getBool(kAppDisableAnimation) ?? false,
fabPosition: prefs.getString(kAppFabPosition) ?? 'center', fabPosition: prefs.getString(kAppFabPosition) ?? 'center',
groupedChatList: prefs.getBool(kAppGroupedChatList) ?? false, groupedChatList: prefs.getBool(kAppGroupedChatList) ?? false,
askedReview: prefs.getBool(kAppAskedReview) ?? false, askedReview: prefs.getBool(kAppAskedReview) ?? false,
firstLaunchAt: prefs.getString(kAppFirstLaunchAt), firstLaunchAt: prefs.getString(kAppFirstLaunchAt),
dashSearchEngine: prefs.getString(kAppDashSearchEngine), dashSearchEngine: prefs.getString(kAppDashSearchEngine),
defaultScreen: prefs.getString(kAppDefaultScreen),
); );
} }
@@ -229,6 +215,12 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
state = state.copyWith(customFonts: value); state = state.copyWith(customFonts: value);
} }
void setDefaultScreen(String? value) {
final prefs = ref.read(sharedPreferencesProvider);
prefs.setString(kAppDefaultScreen, value ?? 'dashboard');
state = state.copyWith(defaultScreen: value);
}
void setAppColorScheme(int? value) { void setAppColorScheme(int? value) {
final prefs = ref.read(sharedPreferencesProvider); final prefs = ref.read(sharedPreferencesProvider);
prefs.setInt(kAppColorSchemeStoreKey, value ?? 0); prefs.setInt(kAppColorSchemeStoreKey, value ?? 0);
@@ -274,12 +266,6 @@ class AppSettingsNotifier extends _$AppSettingsNotifier {
state = state.copyWith(cardTransparency: value); state = state.copyWith(cardTransparency: value);
} }
void setUseMaterial3(bool value) {
final prefs = ref.read(sharedPreferencesProvider);
prefs.setBool(kMaterialYouToggleStoreKey, value);
state = state.copyWith(useMaterial3: value);
}
void setCustomColors(ThemeColors? value) { void setCustomColors(ThemeColors? value) {
final prefs = ref.read(sharedPreferencesProvider); final prefs = ref.read(sharedPreferencesProvider);
if (value != null) { if (value != null) {

View File

@@ -290,7 +290,7 @@ mixin _$AppSettings {
ThemeColors? get customColors; Size? get windowSize;// The window size for desktop platforms ThemeColors? get customColors; Size? get windowSize;// The window size for desktop platforms
double get windowOpacity;// The window opacity for desktop platforms double get windowOpacity;// The window opacity for desktop platforms
double get cardTransparency;// The card background opacity double get cardTransparency;// The card background opacity
String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get useMaterial3; bool get disableAnimation; String get fabPosition; bool get groupedChatList; String? get firstLaunchAt; bool get askedReview; String? get dashSearchEngine; String? get defaultPoolId; String get messageDisplayStyle; String? get themeMode; bool get disableAnimation; String get fabPosition; bool get groupedChatList; String? get firstLaunchAt; bool get askedReview; String? get dashSearchEngine; String? get defaultScreen;
/// Create a copy of AppSettings /// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -301,16 +301,16 @@ $AppSettingsCopyWith<AppSettings> get copyWith => _$AppSettingsCopyWithImpl<AppS
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.useMaterial3, useMaterial3) || other.useMaterial3 == useMaterial3)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.fabPosition, fabPosition) || other.fabPosition == fabPosition)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)); return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.fabPosition, fabPosition) || other.fabPosition == fabPosition)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen));
} }
@override @override
int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,useMaterial3,disableAnimation,fabPosition,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine]); int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,fabPosition,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen]);
@override @override
String toString() { String toString() {
return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, useMaterial3: $useMaterial3, disableAnimation: $disableAnimation, fabPosition: $fabPosition, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine)'; return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, fabPosition: $fabPosition, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen)';
} }
@@ -321,7 +321,7 @@ abstract mixin class $AppSettingsCopyWith<$Res> {
factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl; factory $AppSettingsCopyWith(AppSettings value, $Res Function(AppSettings) _then) = _$AppSettingsCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen
}); });
@@ -338,7 +338,7 @@ class _$AppSettingsCopyWithImpl<$Res>
/// Create a copy of AppSettings /// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? useMaterial3 = null,Object? disableAnimation = null,Object? fabPosition = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? fabPosition = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable
@@ -356,13 +356,13 @@ as double,cardTransparency: null == cardTransparency ? _self.cardTransparency :
as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as String?,useMaterial3: null == useMaterial3 ? _self.useMaterial3 : useMaterial3 // ignore: cast_nullable_to_non_nullable as String?,disableAnimation: null == disableAnimation ? _self.disableAnimation : disableAnimation // ignore: cast_nullable_to_non_nullable
as bool,disableAnimation: null == disableAnimation ? _self.disableAnimation : disableAnimation // ignore: cast_nullable_to_non_nullable
as bool,fabPosition: null == fabPosition ? _self.fabPosition : fabPosition // ignore: cast_nullable_to_non_nullable as bool,fabPosition: null == fabPosition ? _self.fabPosition : fabPosition // ignore: cast_nullable_to_non_nullable
as String,groupedChatList: null == groupedChatList ? _self.groupedChatList : groupedChatList // ignore: cast_nullable_to_non_nullable as String,groupedChatList: null == groupedChatList ? _self.groupedChatList : groupedChatList // ignore: cast_nullable_to_non_nullable
as bool,firstLaunchAt: freezed == firstLaunchAt ? _self.firstLaunchAt : firstLaunchAt // ignore: cast_nullable_to_non_nullable as bool,firstLaunchAt: freezed == firstLaunchAt ? _self.firstLaunchAt : firstLaunchAt // ignore: cast_nullable_to_non_nullable
as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // ignore: cast_nullable_to_non_nullable as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // ignore: cast_nullable_to_non_nullable
as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable
as String?,defaultScreen: freezed == defaultScreen ? _self.defaultScreen : defaultScreen // ignore: cast_nullable_to_non_nullable
as String?, as String?,
)); ));
} }
@@ -457,10 +457,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _AppSettings() when $default != null: case _AppSettings() when $default != null:
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3,_that.disableAnimation,_that.fabPosition,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine);case _: return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.fabPosition,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen);case _:
return orElse(); return orElse();
} }
@@ -478,10 +478,10 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _AppSettings(): case _AppSettings():
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3,_that.disableAnimation,_that.fabPosition,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine);} return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.fabPosition,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
/// ///
@@ -495,10 +495,10 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _AppSettings() when $default != null: case _AppSettings() when $default != null:
return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.useMaterial3,_that.disableAnimation,_that.fabPosition,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine);case _: return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_that.enterToSend,_that.appBarTransparent,_that.showBackgroundImage,_that.notifyWithHaptic,_that.customFonts,_that.appColorScheme,_that.customColors,_that.windowSize,_that.windowOpacity,_that.cardTransparency,_that.defaultPoolId,_that.messageDisplayStyle,_that.themeMode,_that.disableAnimation,_that.fabPosition,_that.groupedChatList,_that.firstLaunchAt,_that.askedReview,_that.dashSearchEngine,_that.defaultScreen);case _:
return null; return null;
} }
@@ -510,7 +510,7 @@ return $default(_that.dataSavingMode,_that.soundEffects,_that.festivalFeatures,_
class _AppSettings implements AppSettings { class _AppSettings implements AppSettings {
const _AppSettings({required this.dataSavingMode, required this.soundEffects, required this.festivalFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.notifyWithHaptic, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.useMaterial3, required this.disableAnimation, required this.fabPosition, required this.groupedChatList, required this.firstLaunchAt, required this.askedReview, required this.dashSearchEngine}); const _AppSettings({required this.dataSavingMode, required this.soundEffects, required this.festivalFeatures, required this.enterToSend, required this.appBarTransparent, required this.showBackgroundImage, required this.notifyWithHaptic, required this.customFonts, required this.appColorScheme, required this.customColors, required this.windowSize, required this.windowOpacity, required this.cardTransparency, required this.defaultPoolId, required this.messageDisplayStyle, required this.themeMode, required this.disableAnimation, required this.fabPosition, required this.groupedChatList, required this.firstLaunchAt, required this.askedReview, required this.dashSearchEngine, required this.defaultScreen});
@override final bool dataSavingMode; @override final bool dataSavingMode;
@@ -533,13 +533,13 @@ class _AppSettings implements AppSettings {
@override final String? defaultPoolId; @override final String? defaultPoolId;
@override final String messageDisplayStyle; @override final String messageDisplayStyle;
@override final String? themeMode; @override final String? themeMode;
@override final bool useMaterial3;
@override final bool disableAnimation; @override final bool disableAnimation;
@override final String fabPosition; @override final String fabPosition;
@override final bool groupedChatList; @override final bool groupedChatList;
@override final String? firstLaunchAt; @override final String? firstLaunchAt;
@override final bool askedReview; @override final bool askedReview;
@override final String? dashSearchEngine; @override final String? dashSearchEngine;
@override final String? defaultScreen;
/// Create a copy of AppSettings /// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@@ -551,16 +551,16 @@ _$AppSettingsCopyWith<_AppSettings> get copyWith => __$AppSettingsCopyWithImpl<_
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.useMaterial3, useMaterial3) || other.useMaterial3 == useMaterial3)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.fabPosition, fabPosition) || other.fabPosition == fabPosition)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettings&&(identical(other.dataSavingMode, dataSavingMode) || other.dataSavingMode == dataSavingMode)&&(identical(other.soundEffects, soundEffects) || other.soundEffects == soundEffects)&&(identical(other.festivalFeatures, festivalFeatures) || other.festivalFeatures == festivalFeatures)&&(identical(other.enterToSend, enterToSend) || other.enterToSend == enterToSend)&&(identical(other.appBarTransparent, appBarTransparent) || other.appBarTransparent == appBarTransparent)&&(identical(other.showBackgroundImage, showBackgroundImage) || other.showBackgroundImage == showBackgroundImage)&&(identical(other.notifyWithHaptic, notifyWithHaptic) || other.notifyWithHaptic == notifyWithHaptic)&&(identical(other.customFonts, customFonts) || other.customFonts == customFonts)&&(identical(other.appColorScheme, appColorScheme) || other.appColorScheme == appColorScheme)&&(identical(other.customColors, customColors) || other.customColors == customColors)&&(identical(other.windowSize, windowSize) || other.windowSize == windowSize)&&(identical(other.windowOpacity, windowOpacity) || other.windowOpacity == windowOpacity)&&(identical(other.cardTransparency, cardTransparency) || other.cardTransparency == cardTransparency)&&(identical(other.defaultPoolId, defaultPoolId) || other.defaultPoolId == defaultPoolId)&&(identical(other.messageDisplayStyle, messageDisplayStyle) || other.messageDisplayStyle == messageDisplayStyle)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.disableAnimation, disableAnimation) || other.disableAnimation == disableAnimation)&&(identical(other.fabPosition, fabPosition) || other.fabPosition == fabPosition)&&(identical(other.groupedChatList, groupedChatList) || other.groupedChatList == groupedChatList)&&(identical(other.firstLaunchAt, firstLaunchAt) || other.firstLaunchAt == firstLaunchAt)&&(identical(other.askedReview, askedReview) || other.askedReview == askedReview)&&(identical(other.dashSearchEngine, dashSearchEngine) || other.dashSearchEngine == dashSearchEngine)&&(identical(other.defaultScreen, defaultScreen) || other.defaultScreen == defaultScreen));
} }
@override @override
int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,useMaterial3,disableAnimation,fabPosition,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine]); int get hashCode => Object.hashAll([runtimeType,dataSavingMode,soundEffects,festivalFeatures,enterToSend,appBarTransparent,showBackgroundImage,notifyWithHaptic,customFonts,appColorScheme,customColors,windowSize,windowOpacity,cardTransparency,defaultPoolId,messageDisplayStyle,themeMode,disableAnimation,fabPosition,groupedChatList,firstLaunchAt,askedReview,dashSearchEngine,defaultScreen]);
@override @override
String toString() { String toString() {
return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, useMaterial3: $useMaterial3, disableAnimation: $disableAnimation, fabPosition: $fabPosition, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine)'; return 'AppSettings(dataSavingMode: $dataSavingMode, soundEffects: $soundEffects, festivalFeatures: $festivalFeatures, enterToSend: $enterToSend, appBarTransparent: $appBarTransparent, showBackgroundImage: $showBackgroundImage, notifyWithHaptic: $notifyWithHaptic, customFonts: $customFonts, appColorScheme: $appColorScheme, customColors: $customColors, windowSize: $windowSize, windowOpacity: $windowOpacity, cardTransparency: $cardTransparency, defaultPoolId: $defaultPoolId, messageDisplayStyle: $messageDisplayStyle, themeMode: $themeMode, disableAnimation: $disableAnimation, fabPosition: $fabPosition, groupedChatList: $groupedChatList, firstLaunchAt: $firstLaunchAt, askedReview: $askedReview, dashSearchEngine: $dashSearchEngine, defaultScreen: $defaultScreen)';
} }
@@ -571,7 +571,7 @@ abstract mixin class _$AppSettingsCopyWith<$Res> implements $AppSettingsCopyWith
factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl; factory _$AppSettingsCopyWith(_AppSettings value, $Res Function(_AppSettings) _then) = __$AppSettingsCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool useMaterial3, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine bool dataSavingMode, bool soundEffects, bool festivalFeatures, bool enterToSend, bool appBarTransparent, bool showBackgroundImage, bool notifyWithHaptic, String? customFonts, int? appColorScheme, ThemeColors? customColors, Size? windowSize, double windowOpacity, double cardTransparency, String? defaultPoolId, String messageDisplayStyle, String? themeMode, bool disableAnimation, String fabPosition, bool groupedChatList, String? firstLaunchAt, bool askedReview, String? dashSearchEngine, String? defaultScreen
}); });
@@ -588,7 +588,7 @@ class __$AppSettingsCopyWithImpl<$Res>
/// Create a copy of AppSettings /// Create a copy of AppSettings
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? useMaterial3 = null,Object? disableAnimation = null,Object? fabPosition = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? dataSavingMode = null,Object? soundEffects = null,Object? festivalFeatures = null,Object? enterToSend = null,Object? appBarTransparent = null,Object? showBackgroundImage = null,Object? notifyWithHaptic = null,Object? customFonts = freezed,Object? appColorScheme = freezed,Object? customColors = freezed,Object? windowSize = freezed,Object? windowOpacity = null,Object? cardTransparency = null,Object? defaultPoolId = freezed,Object? messageDisplayStyle = null,Object? themeMode = freezed,Object? disableAnimation = null,Object? fabPosition = null,Object? groupedChatList = null,Object? firstLaunchAt = freezed,Object? askedReview = null,Object? dashSearchEngine = freezed,Object? defaultScreen = freezed,}) {
return _then(_AppSettings( return _then(_AppSettings(
dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable dataSavingMode: null == dataSavingMode ? _self.dataSavingMode : dataSavingMode // ignore: cast_nullable_to_non_nullable
as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable as bool,soundEffects: null == soundEffects ? _self.soundEffects : soundEffects // ignore: cast_nullable_to_non_nullable
@@ -606,13 +606,13 @@ as double,cardTransparency: null == cardTransparency ? _self.cardTransparency :
as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable as double,defaultPoolId: freezed == defaultPoolId ? _self.defaultPoolId : defaultPoolId // ignore: cast_nullable_to_non_nullable
as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable as String?,messageDisplayStyle: null == messageDisplayStyle ? _self.messageDisplayStyle : messageDisplayStyle // ignore: cast_nullable_to_non_nullable
as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable as String,themeMode: freezed == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as String?,useMaterial3: null == useMaterial3 ? _self.useMaterial3 : useMaterial3 // ignore: cast_nullable_to_non_nullable as String?,disableAnimation: null == disableAnimation ? _self.disableAnimation : disableAnimation // ignore: cast_nullable_to_non_nullable
as bool,disableAnimation: null == disableAnimation ? _self.disableAnimation : disableAnimation // ignore: cast_nullable_to_non_nullable
as bool,fabPosition: null == fabPosition ? _self.fabPosition : fabPosition // ignore: cast_nullable_to_non_nullable as bool,fabPosition: null == fabPosition ? _self.fabPosition : fabPosition // ignore: cast_nullable_to_non_nullable
as String,groupedChatList: null == groupedChatList ? _self.groupedChatList : groupedChatList // ignore: cast_nullable_to_non_nullable as String,groupedChatList: null == groupedChatList ? _self.groupedChatList : groupedChatList // ignore: cast_nullable_to_non_nullable
as bool,firstLaunchAt: freezed == firstLaunchAt ? _self.firstLaunchAt : firstLaunchAt // ignore: cast_nullable_to_non_nullable as bool,firstLaunchAt: freezed == firstLaunchAt ? _self.firstLaunchAt : firstLaunchAt // ignore: cast_nullable_to_non_nullable
as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // ignore: cast_nullable_to_non_nullable as String?,askedReview: null == askedReview ? _self.askedReview : askedReview // ignore: cast_nullable_to_non_nullable
as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable as bool,dashSearchEngine: freezed == dashSearchEngine ? _self.dashSearchEngine : dashSearchEngine // ignore: cast_nullable_to_non_nullable
as String?,defaultScreen: freezed == defaultScreen ? _self.defaultScreen : defaultScreen // ignore: cast_nullable_to_non_nullable
as String?, as String?,
)); ));
} }

View File

@@ -65,7 +65,7 @@ final class AppSettingsNotifierProvider
} }
String _$appSettingsNotifierHash() => String _$appSettingsNotifierHash() =>
r'ee6b67190f3db5d8cb8a9e438a444e91685927d4'; r'ef10d95a9f22e891ad6f5e0225e31508b3eb038e';
abstract class _$AppSettingsNotifier extends $Notifier<AppSettings> { abstract class _$AppSettingsNotifier extends $Notifier<AppSettings> {
AppSettings build(); AppSettings build();

View File

@@ -5,6 +5,7 @@ import 'dart:io';
import 'package:dio_smart_retry/dio_smart_retry.dart'; import 'package:dio_smart_retry/dio_smart_retry.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
@@ -16,6 +17,36 @@ import 'package:island/talker.dart';
import 'config.dart'; import 'config.dart';
part 'network.g.dart';
// Network status enum to track different states
enum NetworkStatus { online, notReady, maintenance, offline }
// Provider for network status using Riverpod v3 annotation
@riverpod
class NetworkStatusNotifier extends _$NetworkStatusNotifier {
@override
NetworkStatus build() {
return NetworkStatus.online;
}
void setOnline() {
state = NetworkStatus.online;
}
void setMaintenance() {
state = NetworkStatus.maintenance;
}
void setOffline() {
state = NetworkStatus.offline;
}
void setNotReady() {
state = NetworkStatus.notReady;
}
}
final imagePickerProvider = Provider((ref) => ImagePicker()); final imagePickerProvider = Provider((ref) => ImagePicker());
final userAgentProvider = FutureProvider<String>((ref) async { final userAgentProvider = FutureProvider<String>((ref) async {
@@ -80,24 +111,66 @@ final apiClientProvider = Provider<Dio>((ref) {
dio.interceptors.addAll([ dio.interceptors.addAll([
InterceptorsWrapper( InterceptorsWrapper(
onRequest: ( onRequest:
RequestOptions options, (RequestOptions options, RequestInterceptorHandler handler) async {
RequestInterceptorHandler handler, try {
) async { final token = await getToken(ref.watch(tokenProvider));
try { if (token != null) {
final token = await getToken(ref.watch(tokenProvider)); options.headers['Authorization'] = 'AtField $token';
if (token != null) { }
options.headers['Authorization'] = 'AtField $token'; } catch (err) {
} // ignore
} catch (err) { }
// ignore
}
final userAgent = ref.read(userAgentProvider); final userAgent = ref.read(userAgentProvider);
if (userAgent.value != null) { if (userAgent.value != null) {
options.headers['User-Agent'] = userAgent.value; options.headers['User-Agent'] = userAgent.value;
}
return handler.next(options);
},
onResponse: (response, handler) {
// Check for 503 status code (Service Unavailable/Maintenance)
if (response.statusCode == 503) {
final networkStatusNotifier = ref.read(
networkStatusProvider.notifier,
);
if (response.headers.value('X-NotReady') != null) {
networkStatusNotifier.setNotReady();
} else {
networkStatusNotifier.setMaintenance();
}
} else if (response.statusCode != null &&
response.statusCode! >= 200 &&
response.statusCode! < 300) {
// Set online status for successful responses
final networkStatusNotifier = ref.read(
networkStatusProvider.notifier,
);
networkStatusNotifier.setOnline();
} }
return handler.next(options); return handler.next(response);
},
onError: (error, handler) {
// Handle network errors and set offline status
if (error.type == DioExceptionType.connectionTimeout ||
error.type == DioExceptionType.receiveTimeout ||
error.type == DioExceptionType.sendTimeout ||
error.type == DioExceptionType.connectionError) {
final networkStatusNotifier = ref.read(
networkStatusProvider.notifier,
);
networkStatusNotifier.setOffline();
} else if (error.response?.statusCode == 503) {
final networkStatusNotifier = ref.read(
networkStatusProvider.notifier,
);
if (error.response?.headers.value('X-NotReady') != null) {
networkStatusNotifier.setNotReady();
} else {
networkStatusNotifier.setMaintenance();
}
}
return handler.next(error);
}, },
), ),
TalkerDioLogger( TalkerDioLogger(

64
lib/pods/network.g.dart Normal file
View File

@@ -0,0 +1,64 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'network.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(NetworkStatusNotifier)
const networkStatusProvider = NetworkStatusNotifierProvider._();
final class NetworkStatusNotifierProvider
extends $NotifierProvider<NetworkStatusNotifier, NetworkStatus> {
const NetworkStatusNotifierProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'networkStatusProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$networkStatusNotifierHash();
@$internal
@override
NetworkStatusNotifier create() => NetworkStatusNotifier();
/// {@macro riverpod.override_with_value}
Override overrideWithValue(NetworkStatus value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<NetworkStatus>(value),
);
}
}
String _$networkStatusNotifierHash() =>
r'6f08e3067fa5265432f28f64e10775e3039506c3';
abstract class _$NetworkStatusNotifier extends $Notifier<NetworkStatus> {
NetworkStatus build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<NetworkStatus, NetworkStatus>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<NetworkStatus, NetworkStatus>,
NetworkStatus,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}

View File

@@ -25,10 +25,9 @@ ThemeSet createAppThemeSet(AppSettings settings) {
} }
ThemeData createAppTheme(Brightness brightness, AppSettings settings) { ThemeData createAppTheme(Brightness brightness, AppSettings settings) {
final seedColor = final seedColor = settings.appColorScheme != null
settings.appColorScheme != null ? Color(settings.appColorScheme!)
? Color(settings.appColorScheme!) : Colors.indigo;
: Colors.indigo;
var colorScheme = ColorScheme.fromSeed( var colorScheme = ColorScheme.fromSeed(
seedColor: seedColor, seedColor: seedColor,
@@ -38,33 +37,33 @@ ThemeData createAppTheme(Brightness brightness, AppSettings settings) {
final customColors = settings.customColors; final customColors = settings.customColors;
if (customColors != null) { if (customColors != null) {
colorScheme = colorScheme.copyWith( colorScheme = colorScheme.copyWith(
primary: primary: customColors.primary != null
customColors.primary != null ? Color(customColors.primary!) : null, ? Color(customColors.primary!)
secondary: : null,
customColors.secondary != null secondary: customColors.secondary != null
? Color(customColors.secondary!) ? Color(customColors.secondary!)
: null, : null,
tertiary: tertiary: customColors.tertiary != null
customColors.tertiary != null ? Color(customColors.tertiary!) : null, ? Color(customColors.tertiary!)
surface: : null,
customColors.surface != null ? Color(customColors.surface!) : null, surface: customColors.surface != null
background: ? Color(customColors.surface!)
customColors.background != null : null,
? Color(customColors.background!) background: customColors.background != null
: null, ? Color(customColors.background!)
: null,
error: customColors.error != null ? Color(customColors.error!) : null, error: customColors.error != null ? Color(customColors.error!) : null,
); );
} }
final hasAppBarTransparent = settings.appBarTransparent; final hasAppBarTransparent = settings.appBarTransparent;
final useM3 = settings.useMaterial3;
final inUseFonts = final inUseFonts =
settings.customFonts?.split(',').map((ele) => ele.trim()).toList() ?? settings.customFonts?.split(',').map((ele) => ele.trim()).toList() ??
['Nunito']; ['Nunito'];
return ThemeData( return ThemeData(
useMaterial3: useM3, useMaterial3: true,
colorScheme: colorScheme, colorScheme: colorScheme,
brightness: brightness, brightness: brightness,
fontFamily: inUseFonts.firstOrNull, fontFamily: inUseFonts.firstOrNull,
@@ -78,10 +77,12 @@ ThemeData createAppTheme(Brightness brightness, AppSettings settings) {
appBarTheme: AppBarTheme( appBarTheme: AppBarTheme(
centerTitle: true, centerTitle: true,
elevation: hasAppBarTransparent ? 0 : null, elevation: hasAppBarTransparent ? 0 : null,
backgroundColor: backgroundColor: hasAppBarTransparent
hasAppBarTransparent ? Colors.transparent : colorScheme.primary, ? Colors.transparent
foregroundColor: : colorScheme.primary,
hasAppBarTransparent ? colorScheme.onSurface : colorScheme.onPrimary, foregroundColor: hasAppBarTransparent
? colorScheme.onSurface
: colorScheme.onPrimary,
), ),
cardTheme: CardThemeData( cardTheme: CardThemeData(
color: colorScheme.surfaceContainer.withOpacity( color: colorScheme.surfaceContainer.withOpacity(

View File

@@ -36,71 +36,41 @@ class UserInfoNotifier extends AsyncNotifier<SnAccount?> {
} }
return user; return user;
} catch (error, stackTrace) { } catch (error, stackTrace) {
if (!kIsWeb) { if (error is DioException) {
if (error is DioException) { if (error.response?.statusCode == 503) return null;
showOverlayDialog<bool>( showOverlayDialog<bool>(
builder: builder: (context, close) => AlertDialog(
(context, close) => AlertDialog( title: Text('failedToLoadUserInfo'.tr()),
title: Text('failedToLoadUserInfo'.tr()), content: Text(
content: Text( [
[ (error.response?.statusCode == 401
(error.response?.statusCode == 401 ? 'failedToLoadUserInfoUnauthorized'
? 'failedToLoadUserInfoUnauthorized' : 'failedToLoadUserInfoNetwork')
: 'failedToLoadUserInfoNetwork') .tr()
.tr() .trim(),
.trim(), '',
'', '${error.response?.statusCode ?? 'Network Error'}',
'${error.response?.statusCode ?? 'Network Error'}', if (error.response?.headers != null) error.response?.headers,
if (error.response?.headers != null) if (error.response?.data != null)
error.response?.headers, jsonEncode(error.response?.data),
if (error.response?.data != null) ].join('\n'),
jsonEncode(error.response?.data), ),
].join('\n'), actions: [
), TextButton(
actions: [ onPressed: () => close(false),
TextButton( child: Text('okay'.tr()),
onPressed: () => close(false), ),
child: Text('okay'.tr()), TextButton(
), onPressed: () => close(true),
TextButton( child: Text('retry'.tr()),
onPressed: () => close(true), ),
child: Text('retry'.tr()), ],
), ),
], ).then((value) {
), if (value == true) {
).then((value) { ref.invalidateSelf();
if (value == true) { }
ref.invalidateSelf(); });
}
});
} else {
showOverlayDialog<bool>(
builder:
(context, close) => AlertDialog(
title: Text('failedToLoadUserInfo'.tr()),
content: Text(
[
'failedToLoadUserInfoNetwork'.tr(),
error.toString(),
].join('\n\n').trim(),
),
actions: [
TextButton(
onPressed: () => close(false),
child: Text('okay'.tr()),
),
TextButton(
onPressed: () => close(true),
child: Text('retry'.tr()),
),
],
),
).then((value) {
if (value == true) {
ref.invalidateSelf();
}
});
}
} }
talker.error( talker.error(
"[UserInfo] Failed to fetch user info...", "[UserInfo] Failed to fetch user info...",

View File

@@ -52,6 +52,11 @@ class WebSocketService {
DateTime? _heartbeatAt; DateTime? _heartbeatAt;
Duration? heartbeatDelay; Duration? heartbeatDelay;
// Reconnection tracking
int _reconnectCount = 0;
DateTime? _reconnectWindowStart;
static const int _maxReconnectsPerMinute = 5;
Stream<WebSocketPacket> get dataStream => _streamController.stream; Stream<WebSocketPacket> get dataStream => _streamController.stream;
Stream<WebSocketState> get statusStream => _statusStreamController.stream; Stream<WebSocketState> get statusStream => _statusStreamController.stream;
@@ -79,8 +84,9 @@ class WebSocketService {
_scheduleHeartbeat(); _scheduleHeartbeat();
_channel!.stream.listen( _channel!.stream.listen(
(data) { (data) {
final dataStr = final dataStr = data is Uint8List
data is Uint8List ? utf8.decode(data) : data.toString(); ? utf8.decode(data)
: data.toString();
final packet = WebSocketPacket.fromJson(jsonDecode(dataStr)); final packet = WebSocketPacket.fromJson(jsonDecode(dataStr));
if (packet.type == 'error.dupe') { if (packet.type == 'error.dupe') {
_statusStreamController.sink.add(WebSocketState.duplicateDevice()); _statusStreamController.sink.add(WebSocketState.duplicateDevice());
@@ -123,6 +129,35 @@ class WebSocketService {
} }
void _scheduleReconnect() { void _scheduleReconnect() {
// Check if we've exceeded the reconnect limit
final now = DateTime.now();
if (_reconnectWindowStart == null ||
now.difference(_reconnectWindowStart!).inMinutes >= 1) {
// Reset window if it's been more than 1 minute since the window started
_reconnectWindowStart = now;
_reconnectCount = 0;
}
_reconnectCount++;
if (_reconnectCount > _maxReconnectsPerMinute) {
talker.error(
'[WebSocket] Reconnect limit exceeded: $_maxReconnectsPerMinute reconnections in the last minute. Stopping auto-reconnect.',
);
_statusStreamController.sink.add(WebSocketState.serverDown());
return;
}
_reconnectTimer?.cancel();
_reconnectTimer = Timer(const Duration(milliseconds: 500), () {
_statusStreamController.sink.add(WebSocketState.connecting());
connect(_ref);
});
}
void manualReconnect() {
_statusStreamController.sink.add(WebSocketState.connecting());
talker.info('[WebSocket] Manual reconnect triggered by user');
_reconnectTimer?.cancel(); _reconnectTimer?.cancel();
_reconnectTimer = Timer(const Duration(milliseconds: 500), () { _reconnectTimer = Timer(const Duration(milliseconds: 500), () {
_statusStreamController.sink.add(WebSocketState.connecting()); _statusStreamController.sink.add(WebSocketState.connecting());
@@ -204,4 +239,9 @@ class WebSocketStateNotifier extends Notifier<WebSocketState> {
_reconnectTimer?.cancel(); _reconnectTimer?.cancel();
state = const WebSocketState.disconnected(); state = const WebSocketState.disconnected();
} }
void manualReconnect() {
final service = ref.read(websocketProvider);
service.manualReconnect();
}
} }

View File

@@ -19,6 +19,7 @@ import 'package:island/screens/files/file_detail.dart';
import 'package:island/screens/posts/post_categories_list.dart'; import 'package:island/screens/posts/post_categories_list.dart';
import 'package:island/screens/posts/post_category_detail.dart'; import 'package:island/screens/posts/post_category_detail.dart';
import 'package:island/screens/posts/post_search.dart'; import 'package:island/screens/posts/post_search.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/app_wrapper.dart'; import 'package:island/widgets/app_wrapper.dart';
import 'package:island/screens/tabs.dart'; import 'package:island/screens/tabs.dart';
import 'package:island/screens/explore.dart'; import 'package:island/screens/explore.dart';
@@ -121,7 +122,12 @@ final routerProvider = Provider<GoRouter>((ref) {
GoRoute( GoRoute(
name: 'logs', name: 'logs',
path: '/logs', path: '/logs',
builder: (context, state) => TalkerScreen(talker: talker), builder: (context, state) => TalkerScreen(
talker: talker,
appBarTitle: 'Debug Logs',
appBarLeading: const PageBackButton(),
theme: TalkerScreenTheme.fromTheme(Theme.of(context)),
),
), ),
// Web articles // Web articles

View File

@@ -368,20 +368,22 @@ class AccountScreen extends HookConsumerWidget {
); );
}, },
}, },
{ if (!isWideScreen(context))
'icon': Symbols.files, {
'title': 'files', 'icon': Symbols.files,
'onTap': () { 'title': 'files',
context.goNamed('files'); 'onTap': () {
context.goNamed('files');
},
}, },
}, if (!isWideScreen(context))
{ {
'icon': Symbols.group, 'icon': Symbols.groups_3,
'title': 'realms', 'title': 'realms',
'onTap': () { 'onTap': () {
context.goNamed('realmList'); context.goNamed('realmList');
},
}, },
},
{ {
'icon': Symbols.wallet, 'icon': Symbols.wallet,
'title': 'wallet', 'title': 'wallet',
@@ -418,67 +420,47 @@ class AccountScreen extends HookConsumerWidget {
}, },
}, },
]; ];
return GridView.builder( return Column(
padding: const EdgeInsets.symmetric(horizontal: 12), children: menuItems.map((item) {
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 80,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: menuItems.length,
itemBuilder: (context, index) {
final item = menuItems[index];
final icon = item['icon'] as IconData; final icon = item['icon'] as IconData;
final title = item['title'] as String; final title = item['title'] as String;
final badgeCount = item['badgeCount'] as int?; final badgeCount = item['badgeCount'] as int?;
final onTap = item['onTap'] as VoidCallback?; final onTap = item['onTap'] as VoidCallback?;
return Card( return ListTile(
margin: EdgeInsets.zero, contentPadding: const EdgeInsets.symmetric(
child: Tooltip( horizontal: 24,
message: title.tr(),
child: InkWell(
borderRadius: BorderRadius.circular(8),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Badge(
isLabelVisible:
badgeCount != null && badgeCount > 0,
label: Text(badgeCount.toString()),
child: Icon(icon, size: 28),
),
],
),
),
),
), ),
trailing: const Icon(Symbols.chevron_right),
dense: true,
leading: Badge(
isLabelVisible: badgeCount != null && badgeCount > 0,
label: Text(badgeCount.toString()),
child: Icon(icon, size: 24),
),
title: Text(title).tr(),
onTap: onTap,
); );
}, }).toList(),
); );
}, },
), ),
const Divider(height: 1).padding(vertical: 8), const Divider(height: 1).padding(vertical: 8),
ListTile( ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.info), leading: const Icon(Symbols.info),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24), contentPadding: EdgeInsets.symmetric(horizontal: 24),
dense: true,
title: Text('about').tr(), title: Text('about').tr(),
onTap: () { onTap: () {
context.pushNamed('about'); context.pushNamed('about');
}, },
), ),
ListTile( ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.bug_report), leading: const Icon(Symbols.bug_report),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24), contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('debugOptions').tr(), title: Text('debugOptions').tr(),
dense: true,
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
useRootNavigator: true, useRootNavigator: true,
@@ -489,11 +471,11 @@ class AccountScreen extends HookConsumerWidget {
}, },
), ),
ListTile( ListTile(
minTileHeight: 48,
leading: const Icon(Symbols.logout), leading: const Icon(Symbols.logout),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.symmetric(horizontal: 24), contentPadding: EdgeInsets.symmetric(horizontal: 24),
title: Text('logout').tr(), title: Text('logout').tr(),
dense: true,
onTap: () async { onTap: () async {
final ws = ref.watch(websocketStateProvider.notifier); final ws = ref.watch(websocketStateProvider.notifier);
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);

View File

@@ -21,6 +21,7 @@ import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/notification_tile.dart'; import 'package:island/widgets/notification_tile.dart';
import 'package:island/widgets/post/post_featured.dart'; import 'package:island/widgets/post/post_featured.dart';
import 'package:island/widgets/check_in.dart'; import 'package:island/widgets/check_in.dart';
import 'package:island/models/activity.dart';
import 'package:island/screens/notification.dart'; import 'package:island/screens/notification.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:slide_countdown/slide_countdown.dart'; import 'package:slide_countdown/slide_countdown.dart';
@@ -229,7 +230,7 @@ class ClockCard extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final time = useState(DateTime.now()); final time = useState(DateTime.now());
final timer = useRef<Timer?>(null); final timer = useRef<Timer?>(null);
final nextNotableDay = ref.watch(nextNotableDayProvider); final notableDay = ref.watch(recentNotableDayProvider);
// Determine icon based on time of day // Determine icon based on time of day
final int hour = time.value.hour; final int hour = time.value.hour;
@@ -301,23 +302,13 @@ class ClockCard extends HookConsumerWidget {
Row( Row(
spacing: 5, spacing: 5,
children: [ children: [
Text('notableDayNext') notableDay.when(
.tr( data: (day) => _buildNotableDayText(context, day!),
args: [ error: (err, _) =>
nextNotableDay.value?.localName ?? 'idk', Text(err.toString()).fontSize(12),
], loading: () =>
) const Text('loading').tr().fontSize(12),
.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(),
),
),
], ],
), ),
], ],
@@ -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 { class NotificationsCard extends HookConsumerWidget {
@@ -493,65 +520,38 @@ class ChatListCard extends HookConsumerWidget {
} }
} }
class FortuneCard extends HookWidget { class FortuneCard extends HookConsumerWidget {
const FortuneCard({super.key}); const FortuneCard({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
final fortune = useMemoized(() { final fortuneAsync = ref.watch(randomFortuneSayingProvider);
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)];
});
return Card( return Card(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)), borderRadius: BorderRadius.all(Radius.circular(12)),
), ),
child: Row( child: fortuneAsync.when(
spacing: 8, loading: () => const Center(child: CircularProgressIndicator()),
mainAxisAlignment: MainAxisAlignment.spaceBetween, error: (error, stack) => Center(child: Text('Error: $error')),
children: [ data: (fortune) {
Expanded( return Row(
child: Text( spacing: 8,
fortune['text']!, mainAxisAlignment: MainAxisAlignment.spaceBetween,
maxLines: 2, children: [
overflow: TextOverflow.fade, Expanded(
), child: Text(
), fortune.content,
Text(fortune['author']!).bold(), maxLines: 2,
], overflow: TextOverflow.fade,
).padding(horizontal: 16), ),
),
Text('—— ${fortune.source}').bold(),
],
).padding(horizontal: 16);
},
),
).height(48); ).height(48);
} }
} }

View File

@@ -834,6 +834,50 @@ class SettingsScreen extends HookConsumerWidget {
), ),
), ),
// Default screen settings
ListTile(
minLeadingWidth: 48,
title: Text('settingsDefaultScreen').tr(),
contentPadding: const EdgeInsets.only(left: 24, right: 17),
leading: const Icon(Symbols.home),
trailing: DropdownButtonHideUnderline(
child: DropdownButton2<String>(
isExpanded: true,
items: [
DropdownMenuItem<String>(
value: 'dashboard',
child: Text('dashboard').tr().fontSize(14),
),
DropdownMenuItem<String>(
value: 'explore',
child: Text('explore').tr().fontSize(14),
),
DropdownMenuItem<String>(
value: 'chat',
child: Text('chat').tr().fontSize(14),
),
DropdownMenuItem<String>(
value: 'account',
child: Text('account').tr().fontSize(14),
),
],
value: settings.defaultScreen ?? 'dashboard',
onChanged: (String? value) {
if (value != null) {
ref.read(appSettingsProvider.notifier).setDefaultScreen(value);
showSnackBar('settingsApplied'.tr());
}
},
buttonStyleData: const ButtonStyleData(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 5),
height: 40,
width: 140,
),
menuItemStyleData: const MenuItemStyleData(height: 40),
),
),
),
// Dash search engine settings // Dash search engine settings
ListTile( ListTile(
isThreeLine: true, isThreeLine: true,

View File

@@ -523,52 +523,113 @@ class _WebSocketIndicator extends HookConsumerWidget {
final isDesktop = final isDesktop =
!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux); !kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);
final devicePadding = MediaQuery.of(context).padding;
final user = ref.watch(userInfoProvider); final user = ref.watch(userInfoProvider);
final websocketState = ref.watch(websocketStateProvider); final websocketState = ref.watch(websocketStateProvider);
final indicatorHeight =
MediaQuery.of(context).padding.top + (isDesktop ? 27.5 : 25);
Color indicatorColor; Color indicatorColor;
String indicatorText; String indicatorText;
Widget indicatorIcon;
bool isInteractive = true;
double opacity = 0.0;
if (websocketState == WebSocketState.connected()) { if (websocketState == WebSocketState.connected()) {
indicatorColor = Colors.green; indicatorColor = Colors.green;
indicatorText = 'connectionConnected'; indicatorText = 'connectionConnected';
indicatorIcon = Icon(
key: ValueKey('ws_connected'),
Symbols.power,
color: Colors.white,
size: 16,
);
opacity = 0.0;
isInteractive = false;
} else if (websocketState == WebSocketState.connecting()) { } else if (websocketState == WebSocketState.connecting()) {
indicatorColor = Colors.teal; indicatorColor = Colors.teal;
indicatorText = 'connectionReconnecting'; indicatorText = 'connectionReconnecting';
indicatorIcon = SizedBox(
key: ValueKey('ws_connecting'),
width: 16,
height: 16,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
strokeWidth: 2,
padding: EdgeInsets.zero,
),
);
opacity = 1.0;
isInteractive = false;
} else if (websocketState == WebSocketState.serverDown()) {
indicatorColor = Colors.red;
indicatorText = 'connectionServerDown';
isInteractive = true;
indicatorIcon = Icon(
key: ValueKey('ws_server_down'),
Symbols.power_off,
color: Colors.white,
size: 16,
);
opacity = 1.0;
} else { } else {
indicatorColor = Colors.red; indicatorColor = Colors.red;
indicatorText = 'connectionDisconnected'; indicatorText = 'connectionDisconnected';
indicatorIcon = Icon(
key: ValueKey('ws_disconnected'),
Symbols.power_off,
color: Colors.white,
size: 16,
);
opacity = 1.0;
isInteractive = false;
} }
return AnimatedPositioned( return Positioned(
duration: Duration(milliseconds: 1850), top: devicePadding.top + (isDesktop ? 27.5 : 25),
top:
user.value == null ||
user.value == null ||
websocketState == WebSocketState.connected()
? -indicatorHeight
: 0,
curve: Curves.fastLinearToSlowEaseIn,
left: 0, left: 0,
right: 0, right: 0,
height: indicatorHeight,
child: IgnorePointer( child: IgnorePointer(
child: Material( ignoring: !isInteractive,
elevation: child: Align(
user.value == null || websocketState == WebSocketState.connected() alignment: Alignment.topCenter,
? 0 child: AnimatedOpacity(
: 4,
child: AnimatedContainer(
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
color: indicatorColor, opacity: opacity,
child: Center( child: Material(
child: Text( elevation:
indicatorText, user.value == null ||
style: TextStyle(color: Colors.white, fontSize: 16), websocketState == WebSocketState.connected()
).tr(), ? 0
).padding(top: MediaQuery.of(context).padding.top), : 4,
borderRadius: BorderRadius.circular(999),
child: GestureDetector(
onTap: () {
ref.read(websocketStateProvider.notifier).manualReconnect();
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: indicatorColor,
borderRadius: BorderRadius.circular(999),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
child: indicatorIcon,
),
Text(
indicatorText,
style: TextStyle(color: Colors.white, fontSize: 13),
).tr(),
],
),
),
),
),
), ),
), ),
), ),

View File

@@ -33,25 +33,43 @@ class AppWrapper extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final networkStateShowing = useState(false); final networkStateShowing = useState(false);
final wsNotifier = ref.watch(websocketStateProvider.notifier);
final websocketState = ref.watch(websocketStateProvider); final websocketState = ref.watch(websocketStateProvider);
final apiState = ref.watch(networkStatusProvider);
final isShowSnow = useState(false); final isShowSnow = useState(false);
final isSnowGone = useState(false); final isSnowGone = useState(false);
// Handle network status modal // Handle network status modal
if (websocketState == WebSocketState.duplicateDevice() && useEffect(() {
!networkStateShowing.value) { bool triedOpen = false;
networkStateShowing.value = true; if (websocketState == WebSocketState.duplicateDevice() &&
WidgetsBinding.instance.addPostFrameCallback((_) { !networkStateShowing.value &&
showModalBottomSheet( !triedOpen) {
context: context, networkStateShowing.value = true;
isScrollControlled: true, WidgetsBinding.instance.addPostFrameCallback((_) {
isDismissible: false, showModalBottomSheet(
builder: (context) => context: context,
NetworkStatusSheet(onReconnect: () => wsNotifier.connect()), isScrollControlled: true,
).then((_) => networkStateShowing.value = false); builder: (context) => NetworkStatusSheet(autoClose: true),
}); ).then((_) => networkStateShowing.value = false);
} });
triedOpen = true;
}
if (apiState != NetworkStatus.online &&
!networkStateShowing.value &&
!triedOpen) {
networkStateShowing.value = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => const NetworkStatusSheet(),
).then((_) => networkStateShowing.value = false);
});
triedOpen = true;
}
return null;
}, [websocketState, apiState]);
// Initialize services and listeners // Initialize services and listeners
useEffect(() { useEffect(() {
@@ -127,6 +145,16 @@ class AppWrapper extends HookConsumerWidget {
final settings = ref.watch(appSettingsProvider); final settings = ref.watch(appSettingsProvider);
final settingsNotifier = ref.watch(appSettingsProvider.notifier); final settingsNotifier = ref.watch(appSettingsProvider.notifier);
useEffect(() {
if (settings.defaultScreen != null &&
settings.defaultScreen != 'dashboard') {
Future(() {
ref.read(routerProvider).goNamed(settings.defaultScreen!);
});
}
return null;
}, []);
final now = DateTime.now(); final now = DateTime.now();
final doesShowSnow = final doesShowSnow =
settings.festivalFeatures && settings.festivalFeatures &&

View File

@@ -8,6 +8,7 @@ import 'package:flutter_hooks/flutter_hooks.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';
import 'package:island/models/activity.dart'; import 'package:island/models/activity.dart';
import 'package:island/models/fortune.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
import 'package:island/screens/auth/captcha.dart'; import 'package:island/screens/auth/captcha.dart';
@@ -42,12 +43,50 @@ Future<SnNotableDay?> nextNotableDay(Ref ref) async {
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
try { try {
final resp = await client.get('/pass/notable/me/next'); 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) { } catch (err) {
return null; return null;
} }
} }
@riverpod
Future<SnNotableDay?> 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<SnFortuneSaying> 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 { class CheckInWidget extends HookConsumerWidget {
final EdgeInsets? margin; final EdgeInsets? margin;
final VoidCallback? onChecked; final VoidCallback? onChecked;

View File

@@ -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?>,
SnNotableDay?,
FutureOr<SnNotableDay?>
>
with $FutureModifier<SnNotableDay?>, $FutureProvider<SnNotableDay?> {
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<SnNotableDay?> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<SnNotableDay?> create(Ref ref) {
return recentNotableDay(ref);
}
}
String _$recentNotableDayHash() => r'780d0f0747d753c5d535d9c2413f8e68d457d974';
@ProviderFor(randomFortuneSaying)
const randomFortuneSayingProvider = RandomFortuneSayingProvider._();
final class RandomFortuneSayingProvider
extends
$FunctionalProvider<
AsyncValue<SnFortuneSaying>,
SnFortuneSaying,
FutureOr<SnFortuneSaying>
>
with $FutureModifier<SnFortuneSaying>, $FutureProvider<SnFortuneSaying> {
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<SnFortuneSaying> $createElement(
$ProviderPointer pointer,
) => $FutureProviderElement(pointer);
@override
FutureOr<SnFortuneSaying> create(Ref ref) {
return randomFortuneSaying(ref);
}
}
String _$randomFortuneSayingHash() =>
r'861378dba8021e8555b568fb8e0390b2b24056f6';

View File

@@ -1,78 +1,227 @@
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:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/pods/websocket.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/content/sheet.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:url_launcher/url_launcher_string.dart';
class NetworkStatusSheet extends HookConsumerWidget { class NetworkStatusSheet extends HookConsumerWidget {
final VoidCallback onReconnect; final bool autoClose;
const NetworkStatusSheet({super.key, this.autoClose = false});
const NetworkStatusSheet({super.key, required this.onReconnect});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final ws = ref.watch(websocketProvider); final ws = ref.watch(websocketProvider);
final wsState = ref.watch(websocketStateProvider); final wsState = ref.watch(websocketStateProvider);
final apiState = ref.watch(networkStatusProvider);
final serverUrl = ref.watch(serverUrlProvider);
final wsNotifier = ref.watch(websocketStateProvider.notifier);
final checks = [
wsState == WebSocketState.connected(),
apiState == NetworkStatus.online,
];
useEffect(() {
if (!autoClose) return;
final checks = [
wsState == WebSocketState.connected(),
apiState == NetworkStatus.online,
];
if (!checks.any((e) => !e)) {
Future.delayed(Duration(seconds: 3), () {
if (context.mounted) Navigator.of(context).pop();
});
}
return null;
}, [wsState, apiState]);
return SheetScaffold( return SheetScaffold(
heightFactor: 0.4, heightFactor: 0.6,
titleText: titleText: !checks.any((e) => !e)
wsState == WebSocketState.connected() ? 'Connection Status'
? 'Connection Status' : 'Connection Issues',
: 'Connection Issue',
child: Padding( child: Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch,
spacing: 4,
children: [ children: [
wsState.when( Container(
connected: decoration: BoxDecoration(
() => Text( borderRadius: BorderRadius.circular(12),
'Connected to server', color: !checks.any((e) => !e)
style: Theme.of(context).textTheme.bodyLarge, ? Colors.green.withOpacity(0.1)
), : Colors.red.withOpacity(0.1),
connecting: ),
() => Text( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
'Connecting to server...', margin: const EdgeInsets.only(bottom: 8),
style: Theme.of(context).textTheme.bodyLarge, child: Column(
), mainAxisSize: MainAxisSize.min,
disconnected: crossAxisAlignment: CrossAxisAlignment.stretch,
() => Text( children: [
'Disconnected from server', Text('overview').tr().bold(),
style: Theme.of(context).textTheme.bodyLarge, Column(
), spacing: 8,
serverDown: mainAxisSize: MainAxisSize.min,
() => Text( crossAxisAlignment: CrossAxisAlignment.stretch,
'The server is not available right now... Please try again later...', children: [
style: Theme.of(context).textTheme.bodyLarge, if (!checks.any((e) => !e))
), Text('Everything is operational.'),
duplicateDevice: if (!checks[0])
() => Text( Text(
'Another device has connected with the same account.', 'WebSocket is disconnected. Realtime updates are not available. You can try tap the reconnect button below to try connect again.',
style: Theme.of(context).textTheme.bodyLarge, ),
), if (!checks[1])
error: ...([
(message) => Text( Text(
'Connection error: $message', 'API is unreachable, you can try again later. If the issue persists, please contact support. Or you can check the service status.',
style: Theme.of(context).textTheme.bodyLarge, ),
InkWell(
onTap: () {
launchUrlString("https://status.solsynth.dev");
},
child: Text(
'Check Service Status',
).textColor(Colors.blueAccent).bold(),
),
]),
],
), ),
],
),
), ),
const SizedBox(height: 16), Row(
if (ws.heartbeatDelay != null) spacing: 8,
Text( children: [
'Last heartbeat: ${ws.heartbeatDelay!.inMilliseconds}ms', Text('WebSocket').bold(),
style: Theme.of(context).textTheme.bodyMedium, wsState.when(
), connected: () => Text('connectionConnected').tr(),
const SizedBox(height: 24), connecting: () => Text('connectionReconnecting').tr(),
Center( disconnected: () => Text('connectionDisconnected').tr(),
child: FilledButton.icon( serverDown: () => Text('connectionServerDown').tr(),
icon: const Icon(Symbols.wifi), duplicateDevice: () => Text(
label: const Text('Reconnect'), 'Another device has connected with the same account.',
onPressed: () { ),
onReconnect(); error: (message) => Text('Connection error: $message'),
Navigator.pop(context); ),
}, if (ws.heartbeatDelay != null)
), Text('${ws.heartbeatDelay!.inMilliseconds}ms'),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: wsState.when(
connected: () => Icon(
Symbols.check_circle,
key: ValueKey(WebSocketState.connected),
color: Colors.green,
size: 16,
),
connecting: () => Icon(
Symbols.sync,
key: ValueKey(WebSocketState.connecting),
color: Colors.orange,
size: 16,
),
disconnected: () => Icon(
Symbols.wifi_off,
key: ValueKey(WebSocketState.disconnected),
color: Colors.grey,
size: 16,
),
serverDown: () => Icon(
Symbols.cloud_off,
key: ValueKey(WebSocketState.serverDown),
color: Colors.red,
size: 16,
),
duplicateDevice: () => Icon(
Symbols.devices,
key: ValueKey(WebSocketState.duplicateDevice),
color: Colors.orange,
size: 16,
),
error: (message) => Icon(
Symbols.error,
key: ValueKey(WebSocketState.error),
color: Colors.red,
size: 16,
),
),
),
],
),
Row(
spacing: 8,
children: [
Text('API').bold(),
Text(
apiState == NetworkStatus.online
? 'Online'
: apiState == NetworkStatus.notReady
? 'Not Ready'
: apiState == NetworkStatus.maintenance
? 'Under Maintenance'
: 'Offline',
),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: apiState == NetworkStatus.online
? Icon(
Symbols.check_circle,
key: ValueKey(NetworkStatus.online),
color: Colors.green,
size: 16,
)
: apiState == NetworkStatus.notReady
? Icon(
Symbols.warning,
key: ValueKey(NetworkStatus.notReady),
color: Colors.orange,
size: 16,
)
: apiState == NetworkStatus.maintenance
? Icon(
Symbols.construction,
key: ValueKey(NetworkStatus.maintenance),
color: Colors.orange,
size: 16,
)
: Icon(
Symbols.cloud_off,
key: ValueKey(NetworkStatus.offline),
color: Colors.red,
size: 16,
),
),
],
),
Row(
spacing: 8,
children: [
Text('API Server').bold(),
Expanded(child: Text(serverUrl)),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
spacing: 8,
children: [
FilledButton.icon(
icon: const Icon(Symbols.wifi),
label: const Text('Reconnect'),
onPressed: () {
wsNotifier.manualReconnect();
},
),
],
), ),
], ],
), ),

View File

@@ -5,7 +5,6 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/message.dart'; import 'package:island/pods/message.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart';
import 'package:island/services/update_service.dart'; import 'package:island/services/update_service.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/network_status_sheet.dart'; import 'package:island/widgets/content/network_status_sheet.dart';
@@ -67,8 +66,6 @@ class DebugSheet extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final wsNotifier = ref.watch(websocketStateProvider.notifier);
return SheetScaffold( return SheetScaffold(
titleText: 'Debug', titleText: 'Debug',
heightFactor: 0.6, heightFactor: 0.6,
@@ -111,10 +108,7 @@ class DebugSheet extends HookConsumerWidget {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
builder: builder: (context) => NetworkStatusSheet(),
(context) => NetworkStatusSheet(
onReconnect: () => wsNotifier.connect(),
),
); );
}, },
), ),

View File

@@ -78,28 +78,24 @@ class FileItem extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
await Navigator.of(context).push( await Navigator.of(context).push(
MaterialPageRoute( MaterialPageRoute(
builder: builder: (context) => Scaffold(
(context) => Scaffold( appBar: AppBar(
appBar: AppBar( title: Text(file.relativePath),
title: Text(file.relativePath), backgroundColor: Colors.transparent,
backgroundColor: Colors.transparent, elevation: 0,
elevation: 0, ),
), extendBodyBehindAppBar: true,
extendBodyBehindAppBar: true, backgroundColor: Colors.black,
backgroundColor: Colors.black, body: PhotoView(
body: PhotoView( imageProvider: CachedNetworkImageProvider(
imageProvider: CachedNetworkImageProvider( imageUrl,
imageUrl, headers: token != null
headers: ? {'Authorization': 'AtField $token'}
token != null : null,
? {'Authorization': 'AtField $token'}
: null,
),
heroAttributes: PhotoViewHeroAttributes(
tag: file.relativePath,
),
),
), ),
heroAttributes: PhotoViewHeroAttributes(tag: file.relativePath),
),
),
), ),
); );
} }
@@ -107,7 +103,16 @@ class FileItem extends HookConsumerWidget {
Future<void> _openFile(BuildContext context, WidgetRef ref) async { Future<void> _openFile(BuildContext context, WidgetRef ref) async {
final ext = file.relativePath.split('.').last.toLowerCase(); final ext = file.relativePath.split('.').last.toLowerCase();
final isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'].contains(ext); final isImage = [
'jpg',
'jpeg',
'png',
'gif',
'webp',
'bmp',
'ico',
'svg',
].contains(ext);
if (isImage) { if (isImage) {
await _showImageViewer(context, ref); await _showImageViewer(context, ref);
@@ -182,41 +187,40 @@ class FileItem extends HookConsumerWidget {
: '${(file.size / 1024).toStringAsFixed(1)} KB', : '${(file.size / 1024).toStringAsFixed(1)} KB',
), ),
trailing: PopupMenuButton<String>( trailing: PopupMenuButton<String>(
itemBuilder: itemBuilder: (context) => [
(context) => [ PopupMenuItem(
PopupMenuItem( value: 'download',
value: 'download', child: Row(
child: Row( children: [
children: [ const Icon(Symbols.download),
const Icon(Symbols.download), const Gap(16),
const Gap(16), Text('Download'),
Text('Download'),
],
),
),
if (!file.isDirectory) ...[
PopupMenuItem(
value: 'edit',
child: Row(
children: [
const Icon(Symbols.edit),
const Gap(16),
Text('Open'),
],
),
),
], ],
PopupMenuItem( ),
value: 'delete', ),
child: Row( if (!file.isDirectory) ...[
children: [ PopupMenuItem(
const Icon(Symbols.delete, color: Colors.red), value: 'edit',
const Gap(16), child: Row(
Text('Delete').textColor(Colors.red), children: [
], const Icon(Symbols.edit),
), const Gap(16),
Text('Open'),
],
), ),
], ),
],
PopupMenuItem(
value: 'delete',
child: Row(
children: [
const Icon(Symbols.delete, color: Colors.red),
const Gap(16),
Text('Delete').textColor(Colors.red),
],
),
),
],
onSelected: (value) async { onSelected: (value) async {
switch (value) { switch (value) {
case 'download': case 'download':
@@ -228,23 +232,22 @@ class FileItem extends HookConsumerWidget {
case 'delete': case 'delete':
final confirmed = await showDialog<bool>( final confirmed = await showDialog<bool>(
context: context, context: context,
builder: builder: (context) => AlertDialog(
(context) => AlertDialog( title: const Text('Delete File'),
title: const Text('Delete File'), content: Text(
content: Text( 'Are you sure you want to delete "${file.relativePath}"?',
'Are you sure you want to delete "${file.relativePath}"?', ),
), actions: [
actions: [ TextButton(
TextButton( onPressed: () => Navigator.of(context).pop(false),
onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel'),
child: const Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Delete'),
),
],
), ),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Delete'),
),
],
),
); );
if (confirmed == true) { if (confirmed == true) {

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 3.5.0+155 version: 3.5.0+156
environment: environment:
sdk: ^3.8.0 sdk: ^3.8.0