Compare commits
15 Commits
3.2.0+127
...
a127b5bace
Author | SHA1 | Date | |
---|---|---|---|
|
a127b5bace | ||
|
b2097cf044 | ||
|
701f29748d | ||
|
9e40ed4600 | ||
|
c90e6fe661 | ||
|
569483300d | ||
|
bab602d98b | ||
|
b4f2bb803a | ||
|
03bfed6f46 | ||
|
f98e5a0aec | ||
|
3d473e2fec | ||
|
0b6efa373a | ||
|
9b60e96cde | ||
|
81cd9b2082 | ||
|
923d5d7514 |
@@ -854,5 +854,26 @@
|
|||||||
"failedToLoadUserInfo": "Failed to load user info",
|
"failedToLoadUserInfo": "Failed to load user info",
|
||||||
"failedToLoadUserInfoNetwork": "It seems be network issue, you can tap the button below to try again.",
|
"failedToLoadUserInfoNetwork": "It seems be network issue, you can tap the button below to try again.",
|
||||||
"failedToLoadUserInfoUnauthorized": "It seems your session has been logged out or not available anymore, you can still try agian to fetch the user info if you want.",
|
"failedToLoadUserInfoUnauthorized": "It seems your session has been logged out or not available anymore, you can still try agian to fetch the user info if you want.",
|
||||||
"okay": "Okay"
|
"okay": "Okay",
|
||||||
|
"postDetails": "Post Details",
|
||||||
|
"postCount": {
|
||||||
|
"zero": "No posts",
|
||||||
|
"one": "{} post",
|
||||||
|
"other": "{} posts"
|
||||||
|
},
|
||||||
|
"mimeType": "MIME Type",
|
||||||
|
"fileSize": "File Size",
|
||||||
|
"fileHash": "File Hash",
|
||||||
|
"exifData": "EXIF Data",
|
||||||
|
"postShuffle": "Shuffle Posts",
|
||||||
|
"leveling": "Leveling",
|
||||||
|
"levelingHistory": "Leveling History",
|
||||||
|
"stellarProgram": "Stellar Program",
|
||||||
|
"socialCredits": "Social Credits",
|
||||||
|
"credits": "Credits",
|
||||||
|
"socialCreditsDescription": "Social Credit is a way for Solar Network to evaluate users. It is calculated based on their behavior and interactions. With a base score of 100, higher scores indicate a user's credibility within the community. Scores change over time to reflect a user's recent behavior. Users with higher credit ratings enjoy more benefits, while users with lower credit ratings may have some functionality restricted.",
|
||||||
|
"socialCreditsLevelPoor": "Poor",
|
||||||
|
"socialCreditsLevelNormal": "Normal",
|
||||||
|
"socialCreditsLevelGood": "Good",
|
||||||
|
"socialCreditsLevelExcellent": "Excellent"
|
||||||
}
|
}
|
||||||
|
@@ -828,5 +828,20 @@
|
|||||||
"failedToLoadUserInfo": "加载用户信息失败",
|
"failedToLoadUserInfo": "加载用户信息失败",
|
||||||
"failedToLoadUserInfoNetwork": "这看起来是个网络问题,你可以按下面的按钮来重试",
|
"failedToLoadUserInfoNetwork": "这看起来是个网络问题,你可以按下面的按钮来重试",
|
||||||
"failedToLoadUserInfoUnauthorized": "看来您的会话已被注销或不再可用,如果您愿意,您仍然可以再次尝试获取用户信息。",
|
"failedToLoadUserInfoUnauthorized": "看来您的会话已被注销或不再可用,如果您愿意,您仍然可以再次尝试获取用户信息。",
|
||||||
"okay": "了解"
|
"okay": "了解",
|
||||||
|
"postDetails": "帖子详情",
|
||||||
|
"mimeType": "类型",
|
||||||
|
"fileSize": "大小",
|
||||||
|
"fileHash": "哈希",
|
||||||
|
"exifData": "EXIF 数据",
|
||||||
|
"leveling": "等级",
|
||||||
|
"levelingHistory": "经验记录",
|
||||||
|
"stellarProgram": "恒星计划",
|
||||||
|
"socialCredits": "社会信用点",
|
||||||
|
"credits": "信用",
|
||||||
|
"socialCreditsDescription": "社会信用是 Solar Network 评价用户的一种方式。它基于用户的行为和互动来计算。以 100 分为基准,分数越高表示用户在社区中的信誉越好。分数会随着时间的推移而变化,反映用户的最新行为。信用等级高的用户可以享受到更多的福利,反之的用户部份功能可能受到限制。",
|
||||||
|
"socialCreditsLevelPoor": "糟糕",
|
||||||
|
"socialCreditsLevelNormal": "正常",
|
||||||
|
"socialCreditsLevelGood": "良好",
|
||||||
|
"socialCreditsLevelExcellent": "优秀"
|
||||||
}
|
}
|
||||||
|
@@ -208,3 +208,37 @@ sealed class SnAuthDeviceWithChallenge with _$SnAuthDeviceWithChallenge {
|
|||||||
factory SnAuthDeviceWithChallenge.fromJson(Map<String, dynamic> json) =>
|
factory SnAuthDeviceWithChallenge.fromJson(Map<String, dynamic> json) =>
|
||||||
_$SnAuthDeviceWithChallengeFromJson(json);
|
_$SnAuthDeviceWithChallengeFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnExperienceRecord with _$SnExperienceRecord {
|
||||||
|
const factory SnExperienceRecord({
|
||||||
|
required String id,
|
||||||
|
required int delta,
|
||||||
|
required String reasonType,
|
||||||
|
required String reason,
|
||||||
|
@Default(1.0) double? bonusMultiplier,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
}) = _SnExperienceRecord;
|
||||||
|
|
||||||
|
factory SnExperienceRecord.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnExperienceRecordFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnSocialCreditRecord with _$SnSocialCreditRecord {
|
||||||
|
const factory SnSocialCreditRecord({
|
||||||
|
required String id,
|
||||||
|
required double delta,
|
||||||
|
required String reasonType,
|
||||||
|
required String reason,
|
||||||
|
required DateTime? expiredAt,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
}) = _SnSocialCreditRecord;
|
||||||
|
|
||||||
|
factory SnSocialCreditRecord.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnSocialCreditRecordFromJson(json);
|
||||||
|
}
|
||||||
|
@@ -3018,6 +3018,562 @@ as bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnExperienceRecord {
|
||||||
|
|
||||||
|
String get id; int get delta; String get reasonType; String get reason; double? get bonusMultiplier; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
|
/// Create a copy of SnExperienceRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnExperienceRecordCopyWith<SnExperienceRecord> get copyWith => _$SnExperienceRecordCopyWithImpl<SnExperienceRecord>(this as SnExperienceRecord, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnExperienceRecord to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnExperienceRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.bonusMultiplier, bonusMultiplier) || other.bonusMultiplier == bonusMultiplier)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,bonusMultiplier,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnExperienceRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, bonusMultiplier: $bonusMultiplier, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnExperienceRecordCopyWith<$Res> {
|
||||||
|
factory $SnExperienceRecordCopyWith(SnExperienceRecord value, $Res Function(SnExperienceRecord) _then) = _$SnExperienceRecordCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnExperienceRecordCopyWithImpl<$Res>
|
||||||
|
implements $SnExperienceRecordCopyWith<$Res> {
|
||||||
|
_$SnExperienceRecordCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnExperienceRecord _self;
|
||||||
|
final $Res Function(SnExperienceRecord) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnExperienceRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? bonusMultiplier = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,bonusMultiplier: freezed == bonusMultiplier ? _self.bonusMultiplier : bonusMultiplier // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnExperienceRecord].
|
||||||
|
extension SnExperienceRecordPatterns on SnExperienceRecord {
|
||||||
|
/// 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( _SnExperienceRecord value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnExperienceRecord() 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( _SnExperienceRecord value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnExperienceRecord():
|
||||||
|
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( _SnExperienceRecord value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnExperienceRecord() 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 id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnExperienceRecord() when $default != null:
|
||||||
|
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.bonusMultiplier,_that.createdAt,_that.updatedAt,_that.deletedAt);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 id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnExperienceRecord():
|
||||||
|
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.bonusMultiplier,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
|
}
|
||||||
|
/// 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 id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnExperienceRecord() when $default != null:
|
||||||
|
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.bonusMultiplier,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnExperienceRecord implements SnExperienceRecord {
|
||||||
|
const _SnExperienceRecord({required this.id, required this.delta, required this.reasonType, required this.reason, this.bonusMultiplier = 1.0, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||||
|
factory _SnExperienceRecord.fromJson(Map<String, dynamic> json) => _$SnExperienceRecordFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final int delta;
|
||||||
|
@override final String reasonType;
|
||||||
|
@override final String reason;
|
||||||
|
@override@JsonKey() final double? bonusMultiplier;
|
||||||
|
@override final DateTime createdAt;
|
||||||
|
@override final DateTime updatedAt;
|
||||||
|
@override final DateTime? deletedAt;
|
||||||
|
|
||||||
|
/// Create a copy of SnExperienceRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnExperienceRecordCopyWith<_SnExperienceRecord> get copyWith => __$SnExperienceRecordCopyWithImpl<_SnExperienceRecord>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnExperienceRecordToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnExperienceRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.bonusMultiplier, bonusMultiplier) || other.bonusMultiplier == bonusMultiplier)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,bonusMultiplier,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnExperienceRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, bonusMultiplier: $bonusMultiplier, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnExperienceRecordCopyWith<$Res> implements $SnExperienceRecordCopyWith<$Res> {
|
||||||
|
factory _$SnExperienceRecordCopyWith(_SnExperienceRecord value, $Res Function(_SnExperienceRecord) _then) = __$SnExperienceRecordCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, int delta, String reasonType, String reason, double? bonusMultiplier, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnExperienceRecordCopyWithImpl<$Res>
|
||||||
|
implements _$SnExperienceRecordCopyWith<$Res> {
|
||||||
|
__$SnExperienceRecordCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnExperienceRecord _self;
|
||||||
|
final $Res Function(_SnExperienceRecord) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnExperienceRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? bonusMultiplier = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_SnExperienceRecord(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,bonusMultiplier: freezed == bonusMultiplier ? _self.bonusMultiplier : bonusMultiplier // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnSocialCreditRecord {
|
||||||
|
|
||||||
|
String get id; double get delta; String get reasonType; String get reason; DateTime? get expiredAt; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
|
/// Create a copy of SnSocialCreditRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnSocialCreditRecordCopyWith<SnSocialCreditRecord> get copyWith => _$SnSocialCreditRecordCopyWithImpl<SnSocialCreditRecord>(this as SnSocialCreditRecord, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnSocialCreditRecord to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnSocialCreditRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,expiredAt,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnSocialCreditRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, expiredAt: $expiredAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnSocialCreditRecordCopyWith<$Res> {
|
||||||
|
factory $SnSocialCreditRecordCopyWith(SnSocialCreditRecord value, $Res Function(SnSocialCreditRecord) _then) = _$SnSocialCreditRecordCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnSocialCreditRecordCopyWithImpl<$Res>
|
||||||
|
implements $SnSocialCreditRecordCopyWith<$Res> {
|
||||||
|
_$SnSocialCreditRecordCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnSocialCreditRecord _self;
|
||||||
|
final $Res Function(SnSocialCreditRecord) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnSocialCreditRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? expiredAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnSocialCreditRecord].
|
||||||
|
extension SnSocialCreditRecordPatterns on SnSocialCreditRecord {
|
||||||
|
/// 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( _SnSocialCreditRecord value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnSocialCreditRecord() 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( _SnSocialCreditRecord value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnSocialCreditRecord():
|
||||||
|
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( _SnSocialCreditRecord value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnSocialCreditRecord() 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 id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnSocialCreditRecord() when $default != null:
|
||||||
|
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);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 id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnSocialCreditRecord():
|
||||||
|
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||||
|
}
|
||||||
|
/// 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 id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnSocialCreditRecord() when $default != null:
|
||||||
|
return $default(_that.id,_that.delta,_that.reasonType,_that.reason,_that.expiredAt,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnSocialCreditRecord implements SnSocialCreditRecord {
|
||||||
|
const _SnSocialCreditRecord({required this.id, required this.delta, required this.reasonType, required this.reason, required this.expiredAt, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||||
|
factory _SnSocialCreditRecord.fromJson(Map<String, dynamic> json) => _$SnSocialCreditRecordFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final double delta;
|
||||||
|
@override final String reasonType;
|
||||||
|
@override final String reason;
|
||||||
|
@override final DateTime? expiredAt;
|
||||||
|
@override final DateTime createdAt;
|
||||||
|
@override final DateTime updatedAt;
|
||||||
|
@override final DateTime? deletedAt;
|
||||||
|
|
||||||
|
/// Create a copy of SnSocialCreditRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnSocialCreditRecordCopyWith<_SnSocialCreditRecord> get copyWith => __$SnSocialCreditRecordCopyWithImpl<_SnSocialCreditRecord>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnSocialCreditRecordToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnSocialCreditRecord&&(identical(other.id, id) || other.id == id)&&(identical(other.delta, delta) || other.delta == delta)&&(identical(other.reasonType, reasonType) || other.reasonType == reasonType)&&(identical(other.reason, reason) || other.reason == reason)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,delta,reasonType,reason,expiredAt,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnSocialCreditRecord(id: $id, delta: $delta, reasonType: $reasonType, reason: $reason, expiredAt: $expiredAt, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnSocialCreditRecordCopyWith<$Res> implements $SnSocialCreditRecordCopyWith<$Res> {
|
||||||
|
factory _$SnSocialCreditRecordCopyWith(_SnSocialCreditRecord value, $Res Function(_SnSocialCreditRecord) _then) = __$SnSocialCreditRecordCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, double delta, String reasonType, String reason, DateTime? expiredAt, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnSocialCreditRecordCopyWithImpl<$Res>
|
||||||
|
implements _$SnSocialCreditRecordCopyWith<$Res> {
|
||||||
|
__$SnSocialCreditRecordCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnSocialCreditRecord _self;
|
||||||
|
final $Res Function(_SnSocialCreditRecord) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnSocialCreditRecord
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? delta = null,Object? reasonType = null,Object? reason = null,Object? expiredAt = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_SnSocialCreditRecord(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,delta: null == delta ? _self.delta : delta // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,reasonType: null == reasonType ? _self.reasonType : reasonType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,reason: null == reason ? _self.reason : reason // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,expiredAt: freezed == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dart format on
|
// dart format on
|
||||||
|
@@ -348,3 +348,62 @@ Map<String, dynamic> _$SnAuthDeviceWithChallengeeToJson(
|
|||||||
'challenges': instance.challenges.map((e) => e.toJson()).toList(),
|
'challenges': instance.challenges.map((e) => e.toJson()).toList(),
|
||||||
'is_current': instance.isCurrent,
|
'is_current': instance.isCurrent,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnExperienceRecord _$SnExperienceRecordFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnExperienceRecord(
|
||||||
|
id: json['id'] as String,
|
||||||
|
delta: (json['delta'] as num).toInt(),
|
||||||
|
reasonType: json['reason_type'] as String,
|
||||||
|
reason: json['reason'] as String,
|
||||||
|
bonusMultiplier: (json['bonus_multiplier'] as num?)?.toDouble() ?? 1.0,
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt:
|
||||||
|
json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnExperienceRecordToJson(_SnExperienceRecord instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'delta': instance.delta,
|
||||||
|
'reason_type': instance.reasonType,
|
||||||
|
'reason': instance.reason,
|
||||||
|
'bonus_multiplier': instance.bonusMultiplier,
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_SnSocialCreditRecord _$SnSocialCreditRecordFromJson(
|
||||||
|
Map<String, dynamic> json,
|
||||||
|
) => _SnSocialCreditRecord(
|
||||||
|
id: json['id'] as String,
|
||||||
|
delta: (json['delta'] as num).toDouble(),
|
||||||
|
reasonType: json['reason_type'] as String,
|
||||||
|
reason: json['reason'] as String,
|
||||||
|
expiredAt:
|
||||||
|
json['expired_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['expired_at'] as String),
|
||||||
|
createdAt: DateTime.parse(json['created_at'] as String),
|
||||||
|
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||||
|
deletedAt:
|
||||||
|
json['deleted_at'] == null
|
||||||
|
? null
|
||||||
|
: DateTime.parse(json['deleted_at'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnSocialCreditRecordToJson(
|
||||||
|
_SnSocialCreditRecord instance,
|
||||||
|
) => <String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'delta': instance.delta,
|
||||||
|
'reason_type': instance.reasonType,
|
||||||
|
'reason': instance.reason,
|
||||||
|
'expired_at': instance.expiredAt?.toIso8601String(),
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
@@ -104,7 +104,7 @@ sealed class SnChatMember with _$SnChatMember {
|
|||||||
sealed class SnChatSummary with _$SnChatSummary {
|
sealed class SnChatSummary with _$SnChatSummary {
|
||||||
const factory SnChatSummary({
|
const factory SnChatSummary({
|
||||||
required int unreadCount,
|
required int unreadCount,
|
||||||
required SnChatMessage lastMessage,
|
required SnChatMessage? lastMessage,
|
||||||
}) = _SnChatSummary;
|
}) = _SnChatSummary;
|
||||||
|
|
||||||
factory SnChatSummary.fromJson(Map<String, dynamic> json) =>
|
factory SnChatSummary.fromJson(Map<String, dynamic> json) =>
|
||||||
|
@@ -1410,7 +1410,7 @@ $SnAccountStatusCopyWith<$Res>? get status {
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnChatSummary {
|
mixin _$SnChatSummary {
|
||||||
|
|
||||||
int get unreadCount; SnChatMessage get lastMessage;
|
int get unreadCount; SnChatMessage? get lastMessage;
|
||||||
/// Create a copy of SnChatSummary
|
/// Create a copy of SnChatSummary
|
||||||
/// 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)
|
||||||
@@ -1443,11 +1443,11 @@ abstract mixin class $SnChatSummaryCopyWith<$Res> {
|
|||||||
factory $SnChatSummaryCopyWith(SnChatSummary value, $Res Function(SnChatSummary) _then) = _$SnChatSummaryCopyWithImpl;
|
factory $SnChatSummaryCopyWith(SnChatSummary value, $Res Function(SnChatSummary) _then) = _$SnChatSummaryCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
int unreadCount, SnChatMessage lastMessage
|
int unreadCount, SnChatMessage? lastMessage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$SnChatMessageCopyWith<$Res> get lastMessage;
|
$SnChatMessageCopyWith<$Res>? get lastMessage;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -1460,20 +1460,23 @@ class _$SnChatSummaryCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnChatSummary
|
/// Create a copy of SnChatSummary
|
||||||
/// 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? unreadCount = null,Object? lastMessage = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? unreadCount = null,Object? lastMessage = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable
|
unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable
|
||||||
as int,lastMessage: null == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable
|
as int,lastMessage: freezed == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as SnChatMessage,
|
as SnChatMessage?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
/// Create a copy of SnChatSummary
|
/// Create a copy of SnChatSummary
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$SnChatMessageCopyWith<$Res> get lastMessage {
|
$SnChatMessageCopyWith<$Res>? get lastMessage {
|
||||||
|
if (_self.lastMessage == null) {
|
||||||
return $SnChatMessageCopyWith<$Res>(_self.lastMessage, (value) {
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnChatMessageCopyWith<$Res>(_self.lastMessage!, (value) {
|
||||||
return _then(_self.copyWith(lastMessage: value));
|
return _then(_self.copyWith(lastMessage: value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1555,7 +1558,7 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage lastMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage? lastMessage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnChatSummary() when $default != null:
|
case _SnChatSummary() when $default != null:
|
||||||
return $default(_that.unreadCount,_that.lastMessage);case _:
|
return $default(_that.unreadCount,_that.lastMessage);case _:
|
||||||
@@ -1576,7 +1579,7 @@ return $default(_that.unreadCount,_that.lastMessage);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage lastMessage) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int unreadCount, SnChatMessage? lastMessage) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnChatSummary():
|
case _SnChatSummary():
|
||||||
return $default(_that.unreadCount,_that.lastMessage);}
|
return $default(_that.unreadCount,_that.lastMessage);}
|
||||||
@@ -1593,7 +1596,7 @@ return $default(_that.unreadCount,_that.lastMessage);}
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int unreadCount, SnChatMessage lastMessage)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int unreadCount, SnChatMessage? lastMessage)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnChatSummary() when $default != null:
|
case _SnChatSummary() when $default != null:
|
||||||
return $default(_that.unreadCount,_that.lastMessage);case _:
|
return $default(_that.unreadCount,_that.lastMessage);case _:
|
||||||
@@ -1612,7 +1615,7 @@ class _SnChatSummary implements SnChatSummary {
|
|||||||
factory _SnChatSummary.fromJson(Map<String, dynamic> json) => _$SnChatSummaryFromJson(json);
|
factory _SnChatSummary.fromJson(Map<String, dynamic> json) => _$SnChatSummaryFromJson(json);
|
||||||
|
|
||||||
@override final int unreadCount;
|
@override final int unreadCount;
|
||||||
@override final SnChatMessage lastMessage;
|
@override final SnChatMessage? lastMessage;
|
||||||
|
|
||||||
/// Create a copy of SnChatSummary
|
/// Create a copy of SnChatSummary
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -1647,11 +1650,11 @@ abstract mixin class _$SnChatSummaryCopyWith<$Res> implements $SnChatSummaryCopy
|
|||||||
factory _$SnChatSummaryCopyWith(_SnChatSummary value, $Res Function(_SnChatSummary) _then) = __$SnChatSummaryCopyWithImpl;
|
factory _$SnChatSummaryCopyWith(_SnChatSummary value, $Res Function(_SnChatSummary) _then) = __$SnChatSummaryCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
int unreadCount, SnChatMessage lastMessage
|
int unreadCount, SnChatMessage? lastMessage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@override $SnChatMessageCopyWith<$Res> get lastMessage;
|
@override $SnChatMessageCopyWith<$Res>? get lastMessage;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -1664,11 +1667,11 @@ class __$SnChatSummaryCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnChatSummary
|
/// Create a copy of SnChatSummary
|
||||||
/// 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? unreadCount = null,Object? lastMessage = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? unreadCount = null,Object? lastMessage = freezed,}) {
|
||||||
return _then(_SnChatSummary(
|
return _then(_SnChatSummary(
|
||||||
unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable
|
unreadCount: null == unreadCount ? _self.unreadCount : unreadCount // ignore: cast_nullable_to_non_nullable
|
||||||
as int,lastMessage: null == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable
|
as int,lastMessage: freezed == lastMessage ? _self.lastMessage : lastMessage // ignore: cast_nullable_to_non_nullable
|
||||||
as SnChatMessage,
|
as SnChatMessage?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1676,9 +1679,12 @@ as SnChatMessage,
|
|||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
$SnChatMessageCopyWith<$Res> get lastMessage {
|
$SnChatMessageCopyWith<$Res>? get lastMessage {
|
||||||
|
if (_self.lastMessage == null) {
|
||||||
return $SnChatMessageCopyWith<$Res>(_self.lastMessage, (value) {
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnChatMessageCopyWith<$Res>(_self.lastMessage!, (value) {
|
||||||
return _then(_self.copyWith(lastMessage: value));
|
return _then(_self.copyWith(lastMessage: value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -213,15 +213,18 @@ Map<String, dynamic> _$SnChatMemberToJson(_SnChatMember instance) =>
|
|||||||
_SnChatSummary _$SnChatSummaryFromJson(Map<String, dynamic> json) =>
|
_SnChatSummary _$SnChatSummaryFromJson(Map<String, dynamic> json) =>
|
||||||
_SnChatSummary(
|
_SnChatSummary(
|
||||||
unreadCount: (json['unread_count'] as num).toInt(),
|
unreadCount: (json['unread_count'] as num).toInt(),
|
||||||
lastMessage: SnChatMessage.fromJson(
|
lastMessage:
|
||||||
json['last_message'] as Map<String, dynamic>,
|
json['last_message'] == null
|
||||||
),
|
? null
|
||||||
|
: SnChatMessage.fromJson(
|
||||||
|
json['last_message'] as Map<String, dynamic>,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SnChatSummaryToJson(_SnChatSummary instance) =>
|
Map<String, dynamic> _$SnChatSummaryToJson(_SnChatSummary instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'unread_count': instance.unreadCount,
|
'unread_count': instance.unreadCount,
|
||||||
'last_message': instance.lastMessage.toJson(),
|
'last_message': instance.lastMessage?.toJson(),
|
||||||
};
|
};
|
||||||
|
|
||||||
_MessageChange _$MessageChangeFromJson(Map<String, dynamic> json) =>
|
_MessageChange _$MessageChangeFromJson(Map<String, dynamic> json) =>
|
||||||
|
@@ -15,6 +15,7 @@ sealed class SnPostCategory with _$SnPostCategory {
|
|||||||
required String slug,
|
required String slug,
|
||||||
String? name,
|
String? name,
|
||||||
@Default([]) List<SnPost> posts,
|
@Default([]) List<SnPost> posts,
|
||||||
|
@Default(0) int usage,
|
||||||
}) = _SnPostCategory;
|
}) = _SnPostCategory;
|
||||||
|
|
||||||
factory SnPostCategory.fromJson(Map<String, dynamic> json) =>
|
factory SnPostCategory.fromJson(Map<String, dynamic> json) =>
|
||||||
|
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnPostCategory {
|
mixin _$SnPostCategory {
|
||||||
|
|
||||||
String get id; String get slug; String? get name; List<SnPost> get posts;
|
String get id; String get slug; String? get name; List<SnPost> get posts; int get usage;
|
||||||
/// Create a copy of SnPostCategory
|
/// Create a copy of SnPostCategory
|
||||||
/// 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 @@ $SnPostCategoryCopyWith<SnPostCategory> get copyWith => _$SnPostCategoryCopyWith
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)&&(identical(other.usage, usage) || other.usage == usage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts));
|
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts),usage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts)';
|
return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ abstract mixin class $SnPostCategoryCopyWith<$Res> {
|
|||||||
factory $SnPostCategoryCopyWith(SnPostCategory value, $Res Function(SnPostCategory) _then) = _$SnPostCategoryCopyWithImpl;
|
factory $SnPostCategoryCopyWith(SnPostCategory value, $Res Function(SnPostCategory) _then) = _$SnPostCategoryCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String slug, String? name, List<SnPost> posts
|
String id, String slug, String? name, List<SnPost> posts, int usage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -65,13 +65,14 @@ class _$SnPostCategoryCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnPostCategory
|
/// Create a copy of SnPostCategory
|
||||||
/// 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? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable
|
as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable
|
||||||
as List<SnPost>,
|
as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,10 +154,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPostCategory() when $default != null:
|
case _SnPostCategory() when $default != null:
|
||||||
return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -174,10 +175,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPostCategory():
|
case _SnPostCategory():
|
||||||
return $default(_that.id,_that.slug,_that.name,_that.posts);}
|
return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -191,10 +192,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);}
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPostCategory() when $default != null:
|
case _SnPostCategory() when $default != null:
|
||||||
return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -206,7 +207,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnPostCategory extends SnPostCategory {
|
class _SnPostCategory extends SnPostCategory {
|
||||||
const _SnPostCategory({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts,super._();
|
const _SnPostCategory({required this.id, required this.slug, this.name, final List<SnPost> posts = const [], this.usage = 0}): _posts = posts,super._();
|
||||||
factory _SnPostCategory.fromJson(Map<String, dynamic> json) => _$SnPostCategoryFromJson(json);
|
factory _SnPostCategory.fromJson(Map<String, dynamic> json) => _$SnPostCategoryFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@@ -219,6 +220,7 @@ class _SnPostCategory extends SnPostCategory {
|
|||||||
return EqualUnmodifiableListView(_posts);
|
return EqualUnmodifiableListView(_posts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override@JsonKey() final int usage;
|
||||||
|
|
||||||
/// Create a copy of SnPostCategory
|
/// Create a copy of SnPostCategory
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -233,16 +235,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostCategory&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)&&(identical(other.usage, usage) || other.usage == usage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts));
|
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts),usage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts)';
|
return 'SnPostCategory(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -253,7 +255,7 @@ abstract mixin class _$SnPostCategoryCopyWith<$Res> implements $SnPostCategoryCo
|
|||||||
factory _$SnPostCategoryCopyWith(_SnPostCategory value, $Res Function(_SnPostCategory) _then) = __$SnPostCategoryCopyWithImpl;
|
factory _$SnPostCategoryCopyWith(_SnPostCategory value, $Res Function(_SnPostCategory) _then) = __$SnPostCategoryCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String slug, String? name, List<SnPost> posts
|
String id, String slug, String? name, List<SnPost> posts, int usage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -270,13 +272,14 @@ class __$SnPostCategoryCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnPostCategory
|
/// Create a copy of SnPostCategory
|
||||||
/// 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? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = null,}) {
|
||||||
return _then(_SnPostCategory(
|
return _then(_SnPostCategory(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable
|
as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable
|
||||||
as List<SnPost>,
|
as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ _SnPostCategory _$SnPostCategoryFromJson(Map<String, dynamic> json) =>
|
|||||||
?.map((e) => SnPost.fromJson(e as Map<String, dynamic>))
|
?.map((e) => SnPost.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
|
usage: (json['usage'] as num?)?.toInt() ?? 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) =>
|
Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) =>
|
||||||
@@ -24,4 +25,5 @@ Map<String, dynamic> _$SnPostCategoryToJson(_SnPostCategory instance) =>
|
|||||||
'slug': instance.slug,
|
'slug': instance.slug,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'posts': instance.posts.map((e) => e.toJson()).toList(),
|
'posts': instance.posts.map((e) => e.toJson()).toList(),
|
||||||
|
'usage': instance.usage,
|
||||||
};
|
};
|
||||||
|
@@ -11,6 +11,7 @@ sealed class SnPostTag with _$SnPostTag {
|
|||||||
required String slug,
|
required String slug,
|
||||||
String? name,
|
String? name,
|
||||||
@Default([]) List<SnPost> posts,
|
@Default([]) List<SnPost> posts,
|
||||||
|
@Default(0) int usage,
|
||||||
}) = _SnPostTag;
|
}) = _SnPostTag;
|
||||||
|
|
||||||
factory SnPostTag.fromJson(Map<String, dynamic> json) =>
|
factory SnPostTag.fromJson(Map<String, dynamic> json) =>
|
||||||
|
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnPostTag {
|
mixin _$SnPostTag {
|
||||||
|
|
||||||
String get id; String get slug; String? get name; List<SnPost> get posts;
|
String get id; String get slug; String? get name; List<SnPost> get posts; int get usage;
|
||||||
/// Create a copy of SnPostTag
|
/// Create a copy of SnPostTag
|
||||||
/// 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 @@ $SnPostTagCopyWith<SnPostTag> get copyWith => _$SnPostTagCopyWithImpl<SnPostTag>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other.posts, posts)&&(identical(other.usage, usage) || other.usage == usage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts));
|
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(posts),usage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts)';
|
return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ abstract mixin class $SnPostTagCopyWith<$Res> {
|
|||||||
factory $SnPostTagCopyWith(SnPostTag value, $Res Function(SnPostTag) _then) = _$SnPostTagCopyWithImpl;
|
factory $SnPostTagCopyWith(SnPostTag value, $Res Function(SnPostTag) _then) = _$SnPostTagCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String slug, String? name, List<SnPost> posts
|
String id, String slug, String? name, List<SnPost> posts, int usage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -65,13 +65,14 @@ class _$SnPostTagCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnPostTag
|
/// Create a copy of SnPostTag
|
||||||
/// 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? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable
|
as String?,posts: null == posts ? _self.posts : posts // ignore: cast_nullable_to_non_nullable
|
||||||
as List<SnPost>,
|
as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,10 +154,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPostTag() when $default != null:
|
case _SnPostTag() when $default != null:
|
||||||
return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -174,10 +175,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String slug, String? name, List<SnPost> posts, int usage) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPostTag():
|
case _SnPostTag():
|
||||||
return $default(_that.id,_that.slug,_that.name,_that.posts);}
|
return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -191,10 +192,10 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);}
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String slug, String? name, List<SnPost> posts, int usage)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _SnPostTag() when $default != null:
|
case _SnPostTag() when $default != null:
|
||||||
return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
return $default(_that.id,_that.slug,_that.name,_that.posts,_that.usage);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -206,7 +207,7 @@ return $default(_that.id,_that.slug,_that.name,_that.posts);case _:
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _SnPostTag implements SnPostTag {
|
class _SnPostTag implements SnPostTag {
|
||||||
const _SnPostTag({required this.id, required this.slug, this.name, final List<SnPost> posts = const []}): _posts = posts;
|
const _SnPostTag({required this.id, required this.slug, this.name, final List<SnPost> posts = const [], this.usage = 0}): _posts = posts;
|
||||||
factory _SnPostTag.fromJson(Map<String, dynamic> json) => _$SnPostTagFromJson(json);
|
factory _SnPostTag.fromJson(Map<String, dynamic> json) => _$SnPostTagFromJson(json);
|
||||||
|
|
||||||
@override final String id;
|
@override final String id;
|
||||||
@@ -219,6 +220,7 @@ class _SnPostTag implements SnPostTag {
|
|||||||
return EqualUnmodifiableListView(_posts);
|
return EqualUnmodifiableListView(_posts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override@JsonKey() final int usage;
|
||||||
|
|
||||||
/// Create a copy of SnPostTag
|
/// Create a copy of SnPostTag
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -233,16 +235,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostTag&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.name, name) || other.name == name)&&const DeepCollectionEquality().equals(other._posts, _posts)&&(identical(other.usage, usage) || other.usage == usage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts));
|
int get hashCode => Object.hash(runtimeType,id,slug,name,const DeepCollectionEquality().hash(_posts),usage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts)';
|
return 'SnPostTag(id: $id, slug: $slug, name: $name, posts: $posts, usage: $usage)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -253,7 +255,7 @@ abstract mixin class _$SnPostTagCopyWith<$Res> implements $SnPostTagCopyWith<$Re
|
|||||||
factory _$SnPostTagCopyWith(_SnPostTag value, $Res Function(_SnPostTag) _then) = __$SnPostTagCopyWithImpl;
|
factory _$SnPostTagCopyWith(_SnPostTag value, $Res Function(_SnPostTag) _then) = __$SnPostTagCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String id, String slug, String? name, List<SnPost> posts
|
String id, String slug, String? name, List<SnPost> posts, int usage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -270,13 +272,14 @@ class __$SnPostTagCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of SnPostTag
|
/// Create a copy of SnPostTag
|
||||||
/// 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? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? name = freezed,Object? posts = null,Object? usage = null,}) {
|
||||||
return _then(_SnPostTag(
|
return _then(_SnPostTag(
|
||||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: freezed == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable
|
as String?,posts: null == posts ? _self._posts : posts // ignore: cast_nullable_to_non_nullable
|
||||||
as List<SnPost>,
|
as List<SnPost>,usage: null == usage ? _self.usage : usage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@ _SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag(
|
|||||||
?.map((e) => SnPost.fromJson(e as Map<String, dynamic>))
|
?.map((e) => SnPost.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
|
usage: (json['usage'] as num?)?.toInt() ?? 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) =>
|
Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) =>
|
||||||
@@ -23,4 +24,5 @@ Map<String, dynamic> _$SnPostTagToJson(_SnPostTag instance) =>
|
|||||||
'slug': instance.slug,
|
'slug': instance.slug,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'posts': instance.posts.map((e) => e.toJson()).toList(),
|
'posts': instance.posts.map((e) => e.toJson()).toList(),
|
||||||
|
'usage': instance.usage,
|
||||||
};
|
};
|
||||||
|
@@ -6,11 +6,13 @@ import 'package:flutter/foundation.dart' show kIsWeb;
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/screens/about.dart';
|
import 'package:island/screens/about.dart';
|
||||||
|
import 'package:island/screens/account/credits.dart';
|
||||||
import 'package:island/screens/developers/apps.dart';
|
import 'package:island/screens/developers/apps.dart';
|
||||||
import 'package:island/screens/developers/edit_app.dart';
|
import 'package:island/screens/developers/edit_app.dart';
|
||||||
import 'package:island/screens/developers/new_app.dart';
|
import 'package:island/screens/developers/new_app.dart';
|
||||||
import 'package:island/screens/developers/hub.dart';
|
import 'package:island/screens/developers/hub.dart';
|
||||||
import 'package:island/screens/discovery/articles.dart';
|
import 'package:island/screens/discovery/articles.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_wrapper.dart';
|
import 'package:island/widgets/app_wrapper.dart';
|
||||||
@@ -52,6 +54,7 @@ import 'package:island/screens/account/event_calendar.dart';
|
|||||||
import 'package:island/screens/discovery/realms.dart';
|
import 'package:island/screens/discovery/realms.dart';
|
||||||
import 'package:island/screens/reports/report_detail.dart';
|
import 'package:island/screens/reports/report_detail.dart';
|
||||||
import 'package:island/screens/reports/report_list.dart';
|
import 'package:island/screens/reports/report_list.dart';
|
||||||
|
import 'package:island/widgets/post/post_shuffle.dart';
|
||||||
|
|
||||||
// Shell route keys for nested navigation
|
// Shell route keys for nested navigation
|
||||||
final rootNavigatorKey = GlobalKey<NavigatorState>();
|
final rootNavigatorKey = GlobalKey<NavigatorState>();
|
||||||
@@ -376,12 +379,14 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
builder: (context, state) => const PostSearchScreen(),
|
builder: (context, state) => const PostSearchScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'postDetail',
|
name: 'postShuffle',
|
||||||
path: '/posts/:id',
|
path: '/posts/shuffle',
|
||||||
builder: (context, state) {
|
builder: (context, state) => const PostShuffleScreen(),
|
||||||
final id = state.pathParameters['id']!;
|
),
|
||||||
return PostDetailScreen(id: id);
|
GoRoute(
|
||||||
},
|
name: 'postCategories',
|
||||||
|
path: '/posts/categories',
|
||||||
|
builder: (context, state) => const PostCategoriesListScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'postCategoryDetail',
|
name: 'postCategoryDetail',
|
||||||
@@ -391,6 +396,11 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
return PostCategoryDetailScreen(slug: slug, isCategory: true);
|
return PostCategoryDetailScreen(slug: slug, isCategory: true);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'postTags',
|
||||||
|
path: '/posts/tags',
|
||||||
|
builder: (context, state) => const PostTagsListScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'postTagDetail',
|
name: 'postTagDetail',
|
||||||
path: '/posts/tags/:slug',
|
path: '/posts/tags/:slug',
|
||||||
@@ -402,6 +412,14 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'postDetail',
|
||||||
|
path: '/posts/:id',
|
||||||
|
builder: (context, state) {
|
||||||
|
final id = state.pathParameters['id']!;
|
||||||
|
return PostDetailScreen(id: id);
|
||||||
|
},
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'publisherProfile',
|
name: 'publisherProfile',
|
||||||
path: '/publishers/:name',
|
path: '/publishers/:name',
|
||||||
@@ -538,6 +556,11 @@ final routerProvider = Provider<GoRouter>((ref) {
|
|||||||
path: '/account/wallet',
|
path: '/account/wallet',
|
||||||
builder: (context, state) => const WalletScreen(),
|
builder: (context, state) => const WalletScreen(),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
name: 'socialCredits',
|
||||||
|
path: '/account/credits',
|
||||||
|
builder: (context, state) => const SocialCreditsScreen(),
|
||||||
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'relationships',
|
name: 'relationships',
|
||||||
path: '/account/relationships',
|
path: '/account/relationships',
|
||||||
|
@@ -236,6 +236,16 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
context.pushNamed('stickerMarketplace');
|
context.pushNamed('stickerMarketplace');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.star),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('credits').tr(),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed('socialCredits');
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
title: Text('abuseReport').tr(),
|
title: Text('abuseReport').tr(),
|
||||||
@@ -389,6 +399,15 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: Text('about').tr(),
|
child: Text('about').tr(),
|
||||||
),
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('debugOptions').tr(),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DebugSheet(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.pushNamed('settings');
|
context.pushNamed('settings');
|
||||||
|
152
lib/screens/account/credits.dart
Normal file
152
lib/screens/account/credits.dart
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/account.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:material_symbols_icons/material_symbols_icons.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
part 'credits.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<double> socialCredits(Ref ref) async {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
final response = await client.get('/id/accounts/me/credits');
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception('Failed to load social credits');
|
||||||
|
}
|
||||||
|
return response.data?.toDouble() ?? 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class SocialCreditHistoryNotifier extends _$SocialCreditHistoryNotifier
|
||||||
|
with CursorPagingNotifierMixin<SnSocialCreditRecord> {
|
||||||
|
static const int _pageSize = 20;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnSocialCreditRecord>> build() => fetch(cursor: null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnSocialCreditRecord>> fetch({
|
||||||
|
required String? cursor,
|
||||||
|
}) async {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||||
|
|
||||||
|
final queryParams = {'offset': offset, 'take': _pageSize};
|
||||||
|
|
||||||
|
final response = await client.get(
|
||||||
|
'/id/accounts/me/credits/history',
|
||||||
|
queryParameters: queryParams,
|
||||||
|
);
|
||||||
|
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||||
|
final List<dynamic> data = response.data;
|
||||||
|
final records =
|
||||||
|
data.map((json) => SnSocialCreditRecord.fromJson(json)).toList();
|
||||||
|
|
||||||
|
final hasMore = offset + records.length < total;
|
||||||
|
final nextCursor = hasMore ? (offset + records.length).toString() : null;
|
||||||
|
|
||||||
|
return CursorPagingData(
|
||||||
|
items: records,
|
||||||
|
hasMore: hasMore,
|
||||||
|
nextCursor: nextCursor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocialCreditsScreen extends HookConsumerWidget {
|
||||||
|
const SocialCreditsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final socialCredits = ref.watch(socialCreditsProvider);
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(title: Text('socialCredits').tr()),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
margin: EdgeInsets.only(left: 16, right: 16, top: 8),
|
||||||
|
child: socialCredits
|
||||||
|
.when(
|
||||||
|
data:
|
||||||
|
(credits) => Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
credits < 100
|
||||||
|
? 'socialCreditsLevelPoor'.tr()
|
||||||
|
: credits < 150
|
||||||
|
? 'socialCreditsLevelNormal'.tr()
|
||||||
|
: credits < 200
|
||||||
|
? 'socialCreditsLevelGood'.tr()
|
||||||
|
: 'socialCreditsLevelExcellent'.tr(),
|
||||||
|
).tr().bold().fontSize(20),
|
||||||
|
Text(
|
||||||
|
'${credits.toStringAsFixed(2)} pts',
|
||||||
|
).fontSize(14),
|
||||||
|
const Gap(8),
|
||||||
|
LinearProgressIndicator(value: credits / 200),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Symbols.info),
|
||||||
|
tooltip: 'socialCreditsDescription'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
error: (_, _) => Text('Error loading credits'),
|
||||||
|
loading: () => const LinearProgressIndicator(),
|
||||||
|
)
|
||||||
|
.padding(horizontal: 20, vertical: 16),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: PagingHelperView(
|
||||||
|
provider: socialCreditHistoryNotifierProvider,
|
||||||
|
futureRefreshable: socialCreditHistoryNotifierProvider.future,
|
||||||
|
notifierRefreshable: socialCreditHistoryNotifierProvider.notifier,
|
||||||
|
contentBuilder:
|
||||||
|
(data, widgetCount, endItemView) => ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: widgetCount,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == widgetCount - 1) {
|
||||||
|
return endItemView;
|
||||||
|
}
|
||||||
|
final record = data.items[index];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text(record.reason),
|
||||||
|
subtitle: Text(
|
||||||
|
DateFormat.yMMMd().format(record.createdAt),
|
||||||
|
),
|
||||||
|
trailing: Text(
|
||||||
|
record.delta > 0
|
||||||
|
? '+${record.delta}'
|
||||||
|
: '${record.delta}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: record.delta > 0 ? Colors.green : Colors.red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
49
lib/screens/account/credits.g.dart
Normal file
49
lib/screens/account/credits.g.dart
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'credits.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$socialCreditsHash() => r'2599844e892127ee4d315caced5c10e4dbaea142';
|
||||||
|
|
||||||
|
/// See also [socialCredits].
|
||||||
|
@ProviderFor(socialCredits)
|
||||||
|
final socialCreditsProvider = AutoDisposeFutureProvider<double>.internal(
|
||||||
|
socialCredits,
|
||||||
|
name: r'socialCreditsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$socialCreditsHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
typedef SocialCreditsRef = AutoDisposeFutureProviderRef<double>;
|
||||||
|
String _$socialCreditHistoryNotifierHash() =>
|
||||||
|
r'950db020754160f835c64cedf3fa2175e61e4d64';
|
||||||
|
|
||||||
|
/// See also [SocialCreditHistoryNotifier].
|
||||||
|
@ProviderFor(SocialCreditHistoryNotifier)
|
||||||
|
final socialCreditHistoryNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||||
|
SocialCreditHistoryNotifier,
|
||||||
|
CursorPagingData<SnSocialCreditRecord>
|
||||||
|
>.internal(
|
||||||
|
SocialCreditHistoryNotifier.new,
|
||||||
|
name: r'socialCreditHistoryNotifierProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$socialCreditHistoryNotifierHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$SocialCreditHistoryNotifier =
|
||||||
|
AutoDisposeAsyncNotifier<CursorPagingData<SnSocialCreditRecord>>;
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/models/wallet.dart';
|
import 'package:island/models/wallet.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';
|
||||||
@@ -19,6 +20,7 @@ import 'package:island/widgets/payment/payment_overlay.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
part 'leveling.g.dart';
|
part 'leveling.g.dart';
|
||||||
@@ -35,13 +37,49 @@ Future<SnWalletSubscription?> accountStellarSubscription(Ref ref) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
class LevelingHistoryNotifier extends _$LevelingHistoryNotifier
|
||||||
|
with CursorPagingNotifierMixin<SnExperienceRecord> {
|
||||||
|
static const int _pageSize = 20;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnExperienceRecord>> build() => fetch(cursor: null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<CursorPagingData<SnExperienceRecord>> fetch({
|
||||||
|
required String? cursor,
|
||||||
|
}) async {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||||
|
|
||||||
|
final queryParams = {'offset': offset, 'take': _pageSize};
|
||||||
|
|
||||||
|
final response = await client.get(
|
||||||
|
'/id/accounts/me/leveling',
|
||||||
|
queryParameters: queryParams,
|
||||||
|
);
|
||||||
|
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||||
|
final List<dynamic> data = response.data;
|
||||||
|
final records =
|
||||||
|
data.map((json) => SnExperienceRecord.fromJson(json)).toList();
|
||||||
|
|
||||||
|
final hasMore = offset + records.length < total;
|
||||||
|
final nextCursor = hasMore ? (offset + records.length).toString() : null;
|
||||||
|
|
||||||
|
return CursorPagingData(
|
||||||
|
items: records,
|
||||||
|
hasMore: hasMore,
|
||||||
|
nextCursor: nextCursor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class LevelingScreen extends HookConsumerWidget {
|
class LevelingScreen extends HookConsumerWidget {
|
||||||
const LevelingScreen({super.key});
|
const LevelingScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final user = ref.watch(userInfoProvider);
|
final user = ref.watch(userInfoProvider);
|
||||||
final stellarSubscription = ref.watch(accountStellarSubscriptionProvider);
|
|
||||||
|
|
||||||
if (user.value == null) {
|
if (user.value == null) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
@@ -50,47 +88,150 @@ class LevelingScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentLevel = user.value!.profile.level;
|
return DefaultTabController(
|
||||||
final currentExp = user.value!.profile.experience;
|
length: 2,
|
||||||
final progress = user.value!.profile.levelingProgress;
|
child: AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('levelingProgress'.tr()),
|
||||||
|
bottom: TabBar(
|
||||||
|
tabs: [
|
||||||
|
Tab(text: 'leveling'.tr()),
|
||||||
|
Tab(text: 'stellarProgram'.tr()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: TabBarView(
|
||||||
|
children: [
|
||||||
|
_buildLevelingTab(context, ref, user.value!),
|
||||||
|
_buildStellarProgramTab(context, ref),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
Widget _buildLevelingTab(
|
||||||
appBar: AppBar(title: Text('levelingProgress'.tr())),
|
BuildContext context,
|
||||||
body: SingleChildScrollView(
|
WidgetRef ref,
|
||||||
padding: getTabbedPadding(context, horizontal: 20, vertical: 20),
|
SnAccount user,
|
||||||
child: Center(
|
) {
|
||||||
child: ConstrainedBox(
|
final currentLevel = user.profile.level;
|
||||||
constraints: const BoxConstraints(maxWidth: 480),
|
final currentExp = user.profile.experience;
|
||||||
child: Column(
|
final progress = user.profile.levelingProgress;
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
// Current Progress Card
|
|
||||||
LevelingProgressCard(
|
|
||||||
level: currentLevel,
|
|
||||||
experience: currentExp,
|
|
||||||
progress: progress,
|
|
||||||
),
|
|
||||||
const Gap(24),
|
|
||||||
|
|
||||||
// Level Stairs Graph
|
return Center(
|
||||||
Text(
|
child: Container(
|
||||||
'levelProgress'.tr(),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
constraints: const BoxConstraints(maxWidth: 480),
|
||||||
fontWeight: FontWeight.bold,
|
child: CustomScrollView(
|
||||||
),
|
slivers: [
|
||||||
),
|
const SliverGap(20),
|
||||||
const Gap(16),
|
|
||||||
|
|
||||||
// Stairs visualization with fixed height and horizontal scroll
|
// Current Progress Card
|
||||||
_buildLevelStairs(context, currentLevel),
|
SliverToBoxAdapter(
|
||||||
|
child: LevelingProgressCard(
|
||||||
const Gap(24),
|
level: currentLevel,
|
||||||
|
experience: currentExp,
|
||||||
// Membership section
|
progress: progress,
|
||||||
_buildMembershipSection(context, ref, stellarSubscription),
|
),
|
||||||
const Gap(16),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
const SliverGap(24),
|
||||||
|
|
||||||
|
// Level Stairs Graph
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Text(
|
||||||
|
'levelProgress'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverGap(16),
|
||||||
|
|
||||||
|
// Stairs visualization with fixed height and horizontal scroll
|
||||||
|
SliverToBoxAdapter(child: _buildLevelStairs(context, currentLevel)),
|
||||||
|
const SliverGap(24),
|
||||||
|
|
||||||
|
// Leveling History
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Text(
|
||||||
|
'levelingHistory'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverGap(8),
|
||||||
|
PagingHelperSliverView(
|
||||||
|
provider: levelingHistoryNotifierProvider,
|
||||||
|
futureRefreshable: levelingHistoryNotifierProvider.future,
|
||||||
|
notifierRefreshable: levelingHistoryNotifierProvider.notifier,
|
||||||
|
contentBuilder:
|
||||||
|
(data, widgetCount, endItemView) => SliverList.builder(
|
||||||
|
itemCount: widgetCount,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == widgetCount - 1) {
|
||||||
|
return endItemView;
|
||||||
|
}
|
||||||
|
final record = data.items[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(record.reason),
|
||||||
|
Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
record.createdAt.formatRelative(context),
|
||||||
|
).fontSize(13),
|
||||||
|
Text('·').fontSize(13).bold(),
|
||||||
|
Text(
|
||||||
|
record.createdAt.formatSystem(),
|
||||||
|
).fontSize(13),
|
||||||
|
],
|
||||||
|
).opacity(0.8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
subtitle: Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'${record.delta > 0 ? '+' : ''}${record.delta} EXP',
|
||||||
|
),
|
||||||
|
if (record.bonusMultiplier != 1.0)
|
||||||
|
Text('x${record.bonusMultiplier}'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
minTileHeight: 56,
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SliverGap(getTabbedPadding(context, vertical: 20).vertical),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStellarProgramTab(BuildContext context, WidgetRef ref) {
|
||||||
|
final stellarSubscription = ref.watch(accountStellarSubscriptionProvider);
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
padding: getTabbedPadding(context, horizontal: 20, vertical: 20),
|
||||||
|
child: Center(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 480),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
_buildMembershipSection(context, ref, stellarSubscription),
|
||||||
|
const Gap(16),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -27,5 +27,26 @@ final accountStellarSubscriptionProvider =
|
|||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef AccountStellarSubscriptionRef =
|
typedef AccountStellarSubscriptionRef =
|
||||||
AutoDisposeFutureProviderRef<SnWalletSubscription?>;
|
AutoDisposeFutureProviderRef<SnWalletSubscription?>;
|
||||||
|
String _$levelingHistoryNotifierHash() =>
|
||||||
|
r'e795f9b7911c9e50f15c095ea237cb0e87bf1e89';
|
||||||
|
|
||||||
|
/// See also [LevelingHistoryNotifier].
|
||||||
|
@ProviderFor(LevelingHistoryNotifier)
|
||||||
|
final levelingHistoryNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||||
|
LevelingHistoryNotifier,
|
||||||
|
CursorPagingData<SnExperienceRecord>
|
||||||
|
>.internal(
|
||||||
|
LevelingHistoryNotifier.new,
|
||||||
|
name: r'levelingHistoryNotifierProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$levelingHistoryNotifierHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef _$LevelingHistoryNotifier =
|
||||||
|
AutoDisposeAsyncNotifier<CursorPagingData<SnExperienceRecord>>;
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||||
|
@@ -79,33 +79,38 @@ class ChatRoomListTile extends HookConsumerWidget {
|
|||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
if (data.lastMessage == null)
|
||||||
spacing: 4,
|
Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1)
|
||||||
children: [
|
else
|
||||||
Badge(
|
Row(
|
||||||
label: Text(data.lastMessage.sender.account.nick),
|
spacing: 4,
|
||||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
children: [
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
Badge(
|
||||||
),
|
label: Text(data.lastMessage!.sender.account.nick),
|
||||||
Expanded(
|
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
child: Text(
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
(data.lastMessage.content?.isNotEmpty ?? false)
|
|
||||||
? data.lastMessage.content!
|
|
||||||
: 'messageNone'.tr(),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Align(
|
child: Text(
|
||||||
alignment: Alignment.centerRight,
|
(data.lastMessage!.content?.isNotEmpty ?? false)
|
||||||
child: Text(
|
? data.lastMessage!.content!
|
||||||
RelativeTime(context).format(data.lastMessage.createdAt),
|
: 'messageNone'.tr(),
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Align(
|
||||||
],
|
alignment: Alignment.centerRight,
|
||||||
),
|
child: Text(
|
||||||
|
RelativeTime(
|
||||||
|
context,
|
||||||
|
).format(data.lastMessage!.createdAt),
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@@ -7,7 +7,7 @@ part of 'room_detail.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$chatMemberListNotifierHash() =>
|
String _$chatMemberListNotifierHash() =>
|
||||||
r'c8fbf4b95df6dae24b1ba21063e9a43351832974';
|
r'3ea30150278523e9f6b23f9200ea9a9fbae9c973';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
|
@@ -106,11 +106,7 @@ class StickerPacksNotifier extends _$StickerPacksNotifier
|
|||||||
try {
|
try {
|
||||||
final response = await client.get(
|
final response = await client.get(
|
||||||
'/sphere/stickers',
|
'/sphere/stickers',
|
||||||
queryParameters: {
|
queryParameters: {'offset': offset, 'take': _pageSize, 'pub': pubName},
|
||||||
'offset': offset,
|
|
||||||
'take': _pageSize,
|
|
||||||
'pubName': pubName,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
final total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||||
|
@@ -148,7 +148,7 @@ class _StickerPackProviderElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$stickerPacksNotifierHash() =>
|
String _$stickerPacksNotifierHash() =>
|
||||||
r'0a8edcf9c35396c411f1214f5e77b1e8fac6a3e6';
|
r'30024b35235f3085a5b1ec2204d0a974ee907e22';
|
||||||
|
|
||||||
abstract class _$StickerPacksNotifier
|
abstract class _$StickerPacksNotifier
|
||||||
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnStickerPack>> {
|
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnStickerPack>> {
|
||||||
|
@@ -173,12 +173,60 @@ class ExploreScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
tooltip: 'webArticlesStand'.tr(),
|
tooltip: 'webArticlesStand'.tr(),
|
||||||
),
|
),
|
||||||
IconButton(
|
PopupMenuButton(
|
||||||
onPressed: () {
|
itemBuilder:
|
||||||
context.pushNamed('postSearch');
|
(context) => [
|
||||||
},
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.category),
|
||||||
|
const Gap(12),
|
||||||
|
Text('categories').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed('postCategories');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.label),
|
||||||
|
const Gap(12),
|
||||||
|
Text('tags').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed('postTags');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.shuffle),
|
||||||
|
const Gap(12),
|
||||||
|
Text('postShuffle').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed('postShuffle');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.search),
|
||||||
|
const Gap(12),
|
||||||
|
Text('search').tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed('postSearch');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Symbols.search,
|
Symbols.action_key,
|
||||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
),
|
),
|
||||||
tooltip: 'search'.tr(),
|
tooltip: 'search'.tr(),
|
||||||
|
242
lib/screens/posts/post_categories_list.dart
Normal file
242
lib/screens/posts/post_categories_list.dart
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/post_category.dart';
|
||||||
|
import 'package:island/models/post_tag.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_paging_utils/riverpod_paging_utils.dart';
|
||||||
|
|
||||||
|
// Post Categories Notifier
|
||||||
|
final postCategoriesNotifierProvider = StateNotifierProvider.autoDispose<
|
||||||
|
PostCategoriesNotifier,
|
||||||
|
AsyncValue<CursorPagingData<SnPostCategory>>
|
||||||
|
>((ref) {
|
||||||
|
return PostCategoriesNotifier(ref);
|
||||||
|
});
|
||||||
|
|
||||||
|
class PostCategoriesNotifier
|
||||||
|
extends StateNotifier<AsyncValue<CursorPagingData<SnPostCategory>>> {
|
||||||
|
final AutoDisposeRef ref;
|
||||||
|
static const int _pageSize = 20;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
PostCategoriesNotifier(this.ref) : super(const AsyncValue.loading()) {
|
||||||
|
state = const AsyncValue.data(
|
||||||
|
CursorPagingData(items: [], hasMore: false, nextCursor: null),
|
||||||
|
);
|
||||||
|
fetch(cursor: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetch({String? cursor}) async {
|
||||||
|
if (_isLoading) return;
|
||||||
|
|
||||||
|
_isLoading = true;
|
||||||
|
if (cursor == null) {
|
||||||
|
state = const AsyncValue.loading();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||||
|
|
||||||
|
final response = await client.get(
|
||||||
|
'/sphere/posts/categories',
|
||||||
|
queryParameters: {
|
||||||
|
'offset': offset,
|
||||||
|
'take': _pageSize,
|
||||||
|
'order': 'usage',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = response.data as List;
|
||||||
|
final categories =
|
||||||
|
data.map((json) => SnPostCategory.fromJson(json)).toList();
|
||||||
|
final hasMore = categories.length == _pageSize;
|
||||||
|
final nextCursor =
|
||||||
|
hasMore ? (offset + categories.length).toString() : null;
|
||||||
|
|
||||||
|
state = AsyncValue.data(
|
||||||
|
CursorPagingData(
|
||||||
|
items: [...(state.value?.items ?? []), ...categories],
|
||||||
|
hasMore: hasMore,
|
||||||
|
nextCursor: nextCursor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e, stack) {
|
||||||
|
state = AsyncValue.error(e, stack);
|
||||||
|
} finally {
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post Tags Notifier
|
||||||
|
final postTagsNotifierProvider = StateNotifierProvider.autoDispose<
|
||||||
|
PostTagsNotifier,
|
||||||
|
AsyncValue<CursorPagingData<SnPostTag>>
|
||||||
|
>((ref) {
|
||||||
|
return PostTagsNotifier(ref);
|
||||||
|
});
|
||||||
|
|
||||||
|
class PostTagsNotifier
|
||||||
|
extends StateNotifier<AsyncValue<CursorPagingData<SnPostTag>>> {
|
||||||
|
final AutoDisposeRef ref;
|
||||||
|
static const int _pageSize = 20;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
PostTagsNotifier(this.ref) : super(const AsyncValue.loading()) {
|
||||||
|
state = const AsyncValue.data(
|
||||||
|
CursorPagingData(items: [], hasMore: false, nextCursor: null),
|
||||||
|
);
|
||||||
|
fetch(cursor: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetch({String? cursor}) async {
|
||||||
|
if (_isLoading) return;
|
||||||
|
|
||||||
|
_isLoading = true;
|
||||||
|
if (cursor == null) {
|
||||||
|
state = const AsyncValue.loading();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final client = ref.read(apiClientProvider);
|
||||||
|
final offset = cursor == null ? 0 : int.parse(cursor);
|
||||||
|
|
||||||
|
final response = await client.get(
|
||||||
|
'/sphere/posts/tags',
|
||||||
|
queryParameters: {
|
||||||
|
'offset': offset,
|
||||||
|
'take': _pageSize,
|
||||||
|
'order': 'usage',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = response.data as List;
|
||||||
|
final tags = data.map((json) => SnPostTag.fromJson(json)).toList();
|
||||||
|
final hasMore = tags.length == _pageSize;
|
||||||
|
final nextCursor = hasMore ? (offset + tags.length).toString() : null;
|
||||||
|
|
||||||
|
state = AsyncValue.data(
|
||||||
|
CursorPagingData(
|
||||||
|
items: [...(state.value?.items ?? []), ...tags],
|
||||||
|
hasMore: hasMore,
|
||||||
|
nextCursor: nextCursor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e, stack) {
|
||||||
|
state = AsyncValue.error(e, stack);
|
||||||
|
} finally {
|
||||||
|
_isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostCategoriesListScreen extends ConsumerWidget {
|
||||||
|
const PostCategoriesListScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final categoriesState = ref.watch(postCategoriesNotifierProvider);
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(title: const Text('categories').tr()),
|
||||||
|
body: categoriesState.when(
|
||||||
|
data: (data) {
|
||||||
|
if (data.items.isEmpty) {
|
||||||
|
return const Center(child: Text('No categories found'));
|
||||||
|
}
|
||||||
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: data.items.length + (data.hasMore ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index >= data.items.length) {
|
||||||
|
ref
|
||||||
|
.read(postCategoriesNotifierProvider.notifier)
|
||||||
|
.fetch(cursor: data.nextCursor);
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
final category = data.items[index];
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(Symbols.category),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
title: Text(category.categoryDisplayTitle),
|
||||||
|
subtitle: Text('postCount'.plural(category.usage)),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed(
|
||||||
|
'postCategoryDetail',
|
||||||
|
pathParameters: {'slug': category.slug},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error:
|
||||||
|
(error, stack) => ResponseErrorWidget(
|
||||||
|
error: error,
|
||||||
|
onRetry: () => ref.invalidate(postCategoriesNotifierProvider),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostTagsListScreen extends ConsumerWidget {
|
||||||
|
const PostTagsListScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tagsState = ref.watch(postTagsNotifierProvider);
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(title: const Text('tags').tr()),
|
||||||
|
body: tagsState.when(
|
||||||
|
data: (data) {
|
||||||
|
if (data.items.isEmpty) {
|
||||||
|
return const Center(child: Text('No tags found'));
|
||||||
|
}
|
||||||
|
return ListView.builder(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: data.items.length + (data.hasMore ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index >= data.items.length) {
|
||||||
|
ref
|
||||||
|
.read(postTagsNotifierProvider.notifier)
|
||||||
|
.fetch(cursor: data.nextCursor);
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
final tag = data.items[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(tag.name ?? '#${tag.slug}'),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
leading: const Icon(Symbols.label),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
subtitle: Text('postCount'.plural(tag.usage)),
|
||||||
|
onTap: () {
|
||||||
|
context.pushNamed(
|
||||||
|
'postTagDetail',
|
||||||
|
pathParameters: {'slug': tag.slug},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error:
|
||||||
|
(error, stack) => ResponseErrorWidget(
|
||||||
|
error: error,
|
||||||
|
onRetry: () => ref.invalidate(postTagsNotifierProvider),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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';
|
||||||
@@ -8,6 +9,7 @@ import 'package:island/widgets/app_scaffold.dart';
|
|||||||
import 'package:island/widgets/post/post_item.dart';
|
import 'package:island/widgets/post/post_item.dart';
|
||||||
import 'package:island/widgets/post/post_quick_reply.dart';
|
import 'package:island/widgets/post/post_quick_reply.dart';
|
||||||
import 'package:island/widgets/post/post_replies.dart';
|
import 'package:island/widgets/post/post_replies.dart';
|
||||||
|
import 'package:island/widgets/response.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
@@ -55,7 +57,10 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
isNoBackground: false,
|
isNoBackground: false,
|
||||||
appBar: AppBar(title: const Text('Post')),
|
appBar: AppBar(
|
||||||
|
leading: const PageBackButton(),
|
||||||
|
title: Text('postDetail').tr(),
|
||||||
|
),
|
||||||
body: postState.when(
|
body: postState.when(
|
||||||
data: (post) {
|
data: (post) {
|
||||||
return Stack(
|
return Stack(
|
||||||
@@ -117,8 +122,12 @@ class PostDetailScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
loading: () => const Center(child: CircularProgressIndicator()),
|
loading: () => ResponseLoadingWidget(),
|
||||||
error: (e, _) => Text('Error: $e'),
|
error:
|
||||||
|
(e, _) => ResponseErrorWidget(
|
||||||
|
error: e,
|
||||||
|
onRetry: () => ref.invalidate(postStateProvider(id)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -399,7 +399,7 @@ class _RealmChatRoomsProviderElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$realmMemberListNotifierHash() =>
|
String _$realmMemberListNotifierHash() =>
|
||||||
r'022bcef5a90cbae05ff23b937851afc3ef913d42';
|
r'2f88f803b2e61e7287ed8a43025173e56ff6ca3b';
|
||||||
|
|
||||||
abstract class _$RealmMemberListNotifier
|
abstract class _$RealmMemberListNotifier
|
||||||
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnRealmMember>> {
|
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnRealmMember>> {
|
||||||
|
@@ -106,7 +106,7 @@ class RealmListScreen extends HookConsumerWidget {
|
|||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 540),
|
constraints: const BoxConstraints(maxWidth: 540),
|
||||||
child: RealmListTile(realm: value[item]),
|
child: RealmListTile(realm: value[item]),
|
||||||
).center();
|
).padding(horizontal: 8).center();
|
||||||
},
|
},
|
||||||
separatorBuilder: (_, _) => const Gap(8),
|
separatorBuilder: (_, _) => const Gap(8),
|
||||||
),
|
),
|
||||||
|
@@ -235,7 +235,11 @@ class PageBackButton extends StatelessWidget {
|
|||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
onWillPop?.call();
|
onWillPop?.call();
|
||||||
context.pop();
|
if (context.canPop()) {
|
||||||
|
context.pop();
|
||||||
|
} else {
|
||||||
|
context.go('/');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
color: color,
|
color: color,
|
||||||
|
@@ -50,6 +50,6 @@ class AppWrapper extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TourTriggerWidget(child: child);
|
return TourTriggerWidget(key: UniqueKey(), child: child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:dismissible_page/dismissible_page.dart';
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_blurhash/flutter_blurhash.dart';
|
import 'package:flutter_blurhash/flutter_blurhash.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
@@ -326,7 +328,11 @@ class CloudFileZoomIn extends HookConsumerWidget {
|
|||||||
|
|
||||||
// Create a temporary file to save the image
|
// Create a temporary file to save the image
|
||||||
final tempDir = await getTemporaryDirectory();
|
final tempDir = await getTemporaryDirectory();
|
||||||
final filePath = '${tempDir.path}/${item.id}.${extension(item.name)}';
|
var extName = extension(item.name).trim();
|
||||||
|
if (extName.isEmpty) {
|
||||||
|
extName = item.mimeType?.split('/').lastOrNull ?? 'jpeg';
|
||||||
|
}
|
||||||
|
final filePath = '${tempDir.path}/${item.id}.$extName';
|
||||||
|
|
||||||
await client.download(
|
await client.download(
|
||||||
'/drive/files/${item.id}',
|
'/drive/files/${item.id}',
|
||||||
@@ -342,39 +348,6 @@ class CloudFileZoomIn extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildInfoRow(IconData icon, String label, String value) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
size: 20,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
value,
|
|
||||||
style: Theme.of(
|
|
||||||
context,
|
|
||||||
).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500),
|
|
||||||
textAlign: TextAlign.end,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String formatFileSize(int bytes) {
|
String formatFileSize(int bytes) {
|
||||||
if (bytes <= 0) return '0 B';
|
if (bytes <= 0) return '0 B';
|
||||||
if (bytes < 1024) return '$bytes B';
|
if (bytes < 1024) return '$bytes B';
|
||||||
@@ -400,57 +373,247 @@ class CloudFileZoomIn extends HookConsumerWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
buildInfoRow(Icons.description, 'Name', item.name),
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('mimeType').tr(),
|
||||||
|
Text(
|
||||||
|
item.mimeType ?? 'unknown'.tr(),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 28, child: const VerticalDivider()),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('fileSize').tr(),
|
||||||
|
Text(
|
||||||
|
formatFileSize(item.size),
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (item.hash != null)
|
||||||
|
SizedBox(height: 28, child: const VerticalDivider()),
|
||||||
|
if (item.hash != null)
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text('fileHash').tr(),
|
||||||
|
Text(
|
||||||
|
'${item.hash!.substring(0, 6)}...',
|
||||||
|
style: theme.textTheme.titleMedium
|
||||||
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: item.hash!),
|
||||||
|
);
|
||||||
|
showSnackBar('File hash copied to clipboard');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 16),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
buildInfoRow(
|
ListTile(
|
||||||
Icons.storage,
|
leading: const Icon(Icons.file_present),
|
||||||
'Size',
|
title: Text('Name').tr(),
|
||||||
formatFileSize(item.size),
|
subtitle: Text(
|
||||||
),
|
item.name,
|
||||||
const Divider(height: 1),
|
maxLines: 1,
|
||||||
buildInfoRow(
|
overflow: TextOverflow.ellipsis,
|
||||||
Icons.category,
|
),
|
||||||
'Type',
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
item.mimeType?.toUpperCase() ?? 'UNKNOWN',
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.copy),
|
||||||
|
onPressed: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: item.name));
|
||||||
|
showSnackBar('File name copied to clipboard');
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (exifData.isNotEmpty) ...[
|
if (exifData.isNotEmpty) ...[
|
||||||
const SizedBox(height: 16),
|
const Divider(height: 1),
|
||||||
Text(
|
Theme(
|
||||||
'EXIF Data',
|
data: theme.copyWith(dividerColor: Colors.transparent),
|
||||||
style: theme.textTheme.titleMedium?.copyWith(
|
child: ExpansionTile(
|
||||||
fontWeight: FontWeight.bold,
|
tilePadding: const EdgeInsets.symmetric(
|
||||||
),
|
horizontal: 24,
|
||||||
).padding(horizontal: 24),
|
),
|
||||||
const SizedBox(height: 8),
|
title: Text(
|
||||||
Column(
|
'exifData'.tr(),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
children: [
|
fontWeight: FontWeight.bold,
|
||||||
...exifData.entries.map(
|
),
|
||||||
(entry) => Padding(
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
children: [
|
||||||
child: Row(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
...exifData.entries.map(
|
||||||
'• ${entry.key.contains('-') ? entry.key.split('-').last : entry.key}: ',
|
(entry) => ListTile(
|
||||||
style: theme.textTheme.bodyMedium?.copyWith(
|
dense: true,
|
||||||
fontWeight: FontWeight.w500,
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
),
|
),
|
||||||
),
|
title:
|
||||||
Expanded(
|
Text(
|
||||||
child: Text(
|
entry.key.contains('-')
|
||||||
|
? entry.key.split('-').last
|
||||||
|
: entry.key,
|
||||||
|
style: theme.textTheme.bodyMedium
|
||||||
|
?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
).bold(),
|
||||||
|
subtitle: Text(
|
||||||
'${entry.value}'.isNotEmpty
|
'${entry.value}'.isNotEmpty
|
||||||
? '${entry.value}'
|
? '${entry.value}'
|
||||||
: 'N/A',
|
: 'N/A',
|
||||||
style: theme.textTheme.bodyMedium,
|
style: theme.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(text: '${entry.value}'),
|
||||||
|
);
|
||||||
|
showSnackBar('Value copied to clipboard');
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (item.fileMeta != null && item.fileMeta!.isNotEmpty) ...[
|
||||||
|
const Divider(height: 1),
|
||||||
|
Theme(
|
||||||
|
data: theme.copyWith(dividerColor: Colors.transparent),
|
||||||
|
child: ExpansionTile(
|
||||||
|
tilePadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'File Metadata',
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
children: [
|
||||||
).padding(horizontal: 24),
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
...item.fileMeta!.entries.map(
|
||||||
|
(entry) => ListTile(
|
||||||
|
dense: true,
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
),
|
||||||
|
title:
|
||||||
|
Text(
|
||||||
|
entry.key,
|
||||||
|
style: theme.textTheme.bodyMedium
|
||||||
|
?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
).bold(),
|
||||||
|
subtitle: Text(
|
||||||
|
jsonEncode(entry.value),
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(
|
||||||
|
text: jsonEncode(entry.value),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
showSnackBar('Value copied to clipboard');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (item.userMeta != null && item.userMeta!.isNotEmpty) ...[
|
||||||
|
const Divider(height: 1),
|
||||||
|
Theme(
|
||||||
|
data: theme.copyWith(dividerColor: Colors.transparent),
|
||||||
|
child: ExpansionTile(
|
||||||
|
tilePadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'User Metadata',
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
...item.userMeta!.entries.map(
|
||||||
|
(entry) => ListTile(
|
||||||
|
dense: true,
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
),
|
||||||
|
title:
|
||||||
|
Text(
|
||||||
|
entry.key,
|
||||||
|
style: theme.textTheme.bodyMedium
|
||||||
|
?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
).bold(),
|
||||||
|
subtitle: Text(
|
||||||
|
jsonEncode(entry.value),
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(
|
||||||
|
ClipboardData(
|
||||||
|
text: jsonEncode(entry.value),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
showSnackBar('Value copied to clipboard');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
|
@@ -8,6 +8,52 @@ import 'package:island/pods/websocket.dart';
|
|||||||
import 'package:island/widgets/content/network_status_sheet.dart';
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:island/pods/config.dart';
|
||||||
|
|
||||||
|
Future<void> _showSetTokenDialog(BuildContext context, WidgetRef ref) async {
|
||||||
|
final TextEditingController controller = TextEditingController();
|
||||||
|
final prefs = ref.read(sharedPreferencesProvider);
|
||||||
|
|
||||||
|
return showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Set Access Token'),
|
||||||
|
content: TextField(
|
||||||
|
controller: controller,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Enter access token',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
autofocus: true,
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Set'),
|
||||||
|
onPressed: () async {
|
||||||
|
final token = controller.text.trim();
|
||||||
|
if (token.isNotEmpty) {
|
||||||
|
await setToken(prefs, token);
|
||||||
|
ref.invalidate(tokenProvider);
|
||||||
|
// Store context in local variable to avoid async gap issue
|
||||||
|
final navigatorContext = context;
|
||||||
|
if (navigatorContext.mounted) {
|
||||||
|
Navigator.of(navigatorContext).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class DebugSheet extends HookConsumerWidget {
|
class DebugSheet extends HookConsumerWidget {
|
||||||
const DebugSheet({super.key});
|
const DebugSheet({super.key});
|
||||||
@@ -49,6 +95,17 @@ class DebugSheet extends HookConsumerWidget {
|
|||||||
Clipboard.setData(ClipboardData(text: tk!.token));
|
Clipboard.setData(ClipboardData(text: tk!.token));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: const Icon(Symbols.edit),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
title: Text('Set access token'),
|
||||||
|
onTap: () async {
|
||||||
|
await _showSetTokenDialog(context, ref);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
ListTile(
|
ListTile(
|
||||||
minTileHeight: 48,
|
minTileHeight: 48,
|
||||||
leading: const Icon(Symbols.delete),
|
leading: const Icon(Symbols.delete),
|
||||||
|
@@ -94,7 +94,7 @@ class PostItemCreator extends HookConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (isOpenable) {
|
if (isOpenable) {
|
||||||
context.pushNamed('postDetail', pathParameters: {'id': item.id});
|
context.goNamed('postDetail', pathParameters: {'id': item.id});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@@ -21,6 +21,7 @@ class PostListNotifier extends _$PostListNotifier
|
|||||||
int? type,
|
int? type,
|
||||||
List<String>? categories,
|
List<String>? categories,
|
||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
|
bool shuffle = false,
|
||||||
}) {
|
}) {
|
||||||
return fetch(cursor: null);
|
return fetch(cursor: null);
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,7 @@ class PostListNotifier extends _$PostListNotifier
|
|||||||
if (type != null) 'type': type,
|
if (type != null) 'type': type,
|
||||||
if (tags != null) 'tags': tags,
|
if (tags != null) 'tags': tags,
|
||||||
if (categories != null) 'categories': categories,
|
if (categories != null) 'categories': categories,
|
||||||
|
if (shuffle) 'shuffle': true,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await client.get(
|
final response = await client.get(
|
||||||
@@ -74,6 +76,7 @@ class SliverPostList extends HookConsumerWidget {
|
|||||||
final int? type;
|
final int? type;
|
||||||
final List<String>? categories;
|
final List<String>? categories;
|
||||||
final List<String>? tags;
|
final List<String>? tags;
|
||||||
|
final bool shuffle;
|
||||||
final PostItemType itemType;
|
final PostItemType itemType;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
@@ -88,6 +91,7 @@ class SliverPostList extends HookConsumerWidget {
|
|||||||
this.type,
|
this.type,
|
||||||
this.categories,
|
this.categories,
|
||||||
this.tags,
|
this.tags,
|
||||||
|
this.shuffle = false,
|
||||||
this.itemType = PostItemType.regular,
|
this.itemType = PostItemType.regular,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.padding,
|
this.padding,
|
||||||
@@ -105,6 +109,7 @@ class SliverPostList extends HookConsumerWidget {
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
),
|
),
|
||||||
futureRefreshable:
|
futureRefreshable:
|
||||||
postListNotifierProvider(
|
postListNotifierProvider(
|
||||||
@@ -113,6 +118,7 @@ class SliverPostList extends HookConsumerWidget {
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
).future,
|
).future,
|
||||||
notifierRefreshable:
|
notifierRefreshable:
|
||||||
postListNotifierProvider(
|
postListNotifierProvider(
|
||||||
@@ -121,6 +127,7 @@ class SliverPostList extends HookConsumerWidget {
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
).notifier,
|
).notifier,
|
||||||
contentBuilder:
|
contentBuilder:
|
||||||
(data, widgetCount, endItemView) => SliverList.builder(
|
(data, widgetCount, endItemView) => SliverList.builder(
|
||||||
|
@@ -6,7 +6,7 @@ part of 'post_list.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$postListNotifierHash() => r'9784b282b3ee14b7109e263c5841a082cf0be78e';
|
String _$postListNotifierHash() => r'faa0b939fae56367ff120ce63d9deb17b1995c9c';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
class _SystemHash {
|
class _SystemHash {
|
||||||
@@ -36,6 +36,7 @@ abstract class _$PostListNotifier
|
|||||||
late final int? type;
|
late final int? type;
|
||||||
late final List<String>? categories;
|
late final List<String>? categories;
|
||||||
late final List<String>? tags;
|
late final List<String>? tags;
|
||||||
|
late final bool shuffle;
|
||||||
|
|
||||||
FutureOr<CursorPagingData<SnPost>> build({
|
FutureOr<CursorPagingData<SnPost>> build({
|
||||||
String? pubName,
|
String? pubName,
|
||||||
@@ -43,6 +44,7 @@ abstract class _$PostListNotifier
|
|||||||
int? type,
|
int? type,
|
||||||
List<String>? categories,
|
List<String>? categories,
|
||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
|
bool shuffle = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ class PostListNotifierFamily
|
|||||||
int? type,
|
int? type,
|
||||||
List<String>? categories,
|
List<String>? categories,
|
||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
|
bool shuffle = false,
|
||||||
}) {
|
}) {
|
||||||
return PostListNotifierProvider(
|
return PostListNotifierProvider(
|
||||||
pubName: pubName,
|
pubName: pubName,
|
||||||
@@ -70,6 +73,7 @@ class PostListNotifierFamily
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +87,7 @@ class PostListNotifierFamily
|
|||||||
type: provider.type,
|
type: provider.type,
|
||||||
categories: provider.categories,
|
categories: provider.categories,
|
||||||
tags: provider.tags,
|
tags: provider.tags,
|
||||||
|
shuffle: provider.shuffle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +120,7 @@ class PostListNotifierProvider
|
|||||||
int? type,
|
int? type,
|
||||||
List<String>? categories,
|
List<String>? categories,
|
||||||
List<String>? tags,
|
List<String>? tags,
|
||||||
|
bool shuffle = false,
|
||||||
}) : this._internal(
|
}) : this._internal(
|
||||||
() =>
|
() =>
|
||||||
PostListNotifier()
|
PostListNotifier()
|
||||||
@@ -122,7 +128,8 @@ class PostListNotifierProvider
|
|||||||
..realm = realm
|
..realm = realm
|
||||||
..type = type
|
..type = type
|
||||||
..categories = categories
|
..categories = categories
|
||||||
..tags = tags,
|
..tags = tags
|
||||||
|
..shuffle = shuffle,
|
||||||
from: postListNotifierProvider,
|
from: postListNotifierProvider,
|
||||||
name: r'postListNotifierProvider',
|
name: r'postListNotifierProvider',
|
||||||
debugGetCreateSourceHash:
|
debugGetCreateSourceHash:
|
||||||
@@ -137,6 +144,7 @@ class PostListNotifierProvider
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
);
|
);
|
||||||
|
|
||||||
PostListNotifierProvider._internal(
|
PostListNotifierProvider._internal(
|
||||||
@@ -151,6 +159,7 @@ class PostListNotifierProvider
|
|||||||
required this.type,
|
required this.type,
|
||||||
required this.categories,
|
required this.categories,
|
||||||
required this.tags,
|
required this.tags,
|
||||||
|
required this.shuffle,
|
||||||
}) : super.internal();
|
}) : super.internal();
|
||||||
|
|
||||||
final String? pubName;
|
final String? pubName;
|
||||||
@@ -158,6 +167,7 @@ class PostListNotifierProvider
|
|||||||
final int? type;
|
final int? type;
|
||||||
final List<String>? categories;
|
final List<String>? categories;
|
||||||
final List<String>? tags;
|
final List<String>? tags;
|
||||||
|
final bool shuffle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<CursorPagingData<SnPost>> runNotifierBuild(
|
FutureOr<CursorPagingData<SnPost>> runNotifierBuild(
|
||||||
@@ -169,6 +179,7 @@ class PostListNotifierProvider
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +194,8 @@ class PostListNotifierProvider
|
|||||||
..realm = realm
|
..realm = realm
|
||||||
..type = type
|
..type = type
|
||||||
..categories = categories
|
..categories = categories
|
||||||
..tags = tags,
|
..tags = tags
|
||||||
|
..shuffle = shuffle,
|
||||||
from: from,
|
from: from,
|
||||||
name: null,
|
name: null,
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
@@ -194,6 +206,7 @@ class PostListNotifierProvider
|
|||||||
type: type,
|
type: type,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
shuffle: shuffle,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -214,7 +227,8 @@ class PostListNotifierProvider
|
|||||||
other.realm == realm &&
|
other.realm == realm &&
|
||||||
other.type == type &&
|
other.type == type &&
|
||||||
other.categories == categories &&
|
other.categories == categories &&
|
||||||
other.tags == tags;
|
other.tags == tags &&
|
||||||
|
other.shuffle == shuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -225,6 +239,7 @@ class PostListNotifierProvider
|
|||||||
hash = _SystemHash.combine(hash, type.hashCode);
|
hash = _SystemHash.combine(hash, type.hashCode);
|
||||||
hash = _SystemHash.combine(hash, categories.hashCode);
|
hash = _SystemHash.combine(hash, categories.hashCode);
|
||||||
hash = _SystemHash.combine(hash, tags.hashCode);
|
hash = _SystemHash.combine(hash, tags.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, shuffle.hashCode);
|
||||||
|
|
||||||
return _SystemHash.finish(hash);
|
return _SystemHash.finish(hash);
|
||||||
}
|
}
|
||||||
@@ -248,6 +263,9 @@ mixin PostListNotifierRef
|
|||||||
|
|
||||||
/// The parameter `tags` of this provider.
|
/// The parameter `tags` of this provider.
|
||||||
List<String>? get tags;
|
List<String>? get tags;
|
||||||
|
|
||||||
|
/// The parameter `shuffle` of this provider.
|
||||||
|
bool get shuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PostListNotifierProviderElement
|
class _PostListNotifierProviderElement
|
||||||
@@ -270,6 +288,8 @@ class _PostListNotifierProviderElement
|
|||||||
(origin as PostListNotifierProvider).categories;
|
(origin as PostListNotifierProvider).categories;
|
||||||
@override
|
@override
|
||||||
List<String>? get tags => (origin as PostListNotifierProvider).tags;
|
List<String>? get tags => (origin as PostListNotifierProvider).tags;
|
||||||
|
@override
|
||||||
|
bool get shuffle => (origin as PostListNotifierProvider).shuffle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
|
108
lib/widgets/post/post_shuffle.dart
Normal file
108
lib/widgets/post/post_shuffle.dart
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_card_swiper/flutter_card_swiper.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/post/post_item.dart';
|
||||||
|
import 'package:island/widgets/post/post_list.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
class PostShuffleScreen extends HookConsumerWidget {
|
||||||
|
const PostShuffleScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final postListState = ref.watch(postListNotifierProvider(shuffle: true));
|
||||||
|
final postListNotifier = ref.watch(
|
||||||
|
postListNotifierProvider(shuffle: true).notifier,
|
||||||
|
);
|
||||||
|
|
||||||
|
final cardSwiperController = useMemoized(() => CardSwiperController(), []);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
return cardSwiperController.dispose;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const kBottomControlHeight = 96.0;
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(title: const Text('postShuffle').tr()),
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom:
|
||||||
|
kBottomControlHeight + MediaQuery.of(context).padding.bottom,
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
(postListState.value?.items.length ?? 0) > 0
|
||||||
|
? CardSwiper(
|
||||||
|
controller: cardSwiperController,
|
||||||
|
cardsCount: postListState.value!.items.length,
|
||||||
|
cardBuilder: (
|
||||||
|
context,
|
||||||
|
index,
|
||||||
|
horizontalOffsetPercentage,
|
||||||
|
verticalOffsetPercentage,
|
||||||
|
) {
|
||||||
|
return Center(
|
||||||
|
child: Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
child: PostActionableItem(
|
||||||
|
item: postListState.value!.items[index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onEnd: () {
|
||||||
|
if (postListState.value?.hasMore ?? true) {
|
||||||
|
postListNotifier.fetch(
|
||||||
|
cursor: postListState.value?.nextCursor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.of(context).padding.bottom,
|
||||||
|
),
|
||||||
|
height: kBottomControlHeight,
|
||||||
|
child:
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
cardSwiperController.undo();
|
||||||
|
},
|
||||||
|
icon: const Icon(Symbols.arrow_left_alt),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
cardSwiperController.swipe(CardSwiperDirection.right);
|
||||||
|
},
|
||||||
|
icon: const Icon(Symbols.arrow_right_alt),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(all: 8).center(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
52
pubspec.lock
52
pubspec.lock
@@ -565,10 +565,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: ef7d2a085c1b1d69d17b6842d0734aad90156de08df6bd3c12496d0bd6ddf8e2
|
sha256: e7e16c9d15c36330b94ca0e2ad8cb61f93cd5282d0158c09805aed13b5452f22
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.3.1"
|
version: "10.3.2"
|
||||||
file_selector_linux:
|
file_selector_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -734,6 +734,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.1"
|
||||||
|
flutter_card_swiper:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_card_swiper
|
||||||
|
sha256: "1eacbfab31b572223042e03409726553aec431abe48af48c8d591d376d070d3d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.2"
|
||||||
flutter_colorpicker:
|
flutter_colorpicker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -754,10 +762,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_hooks
|
name: flutter_hooks
|
||||||
sha256: c3df76c62bb3a9f9bee75c57cdab40abab6123b734c1cd7e9b26a5dbd436eceb
|
sha256: "8ae1f090e5f4ef5cfa6670ce1ab5dddadd33f3533a7f9ba19d9f958aa2a89f42"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.21.3"
|
version: "0.21.3+1"
|
||||||
flutter_inappwebview:
|
flutter_inappwebview:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1137,10 +1145,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: "8b1f37dfaf6e958c6b872322db06f946509433bec3de753c3491a42ae9ec2b48"
|
sha256: ced3fdc143c1437234ac3b8e985f3286cf138968bb83ca9a6f94d22f2951c6b9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "16.1.0"
|
version: "16.2.0"
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1497,10 +1505,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: material_symbols_icons
|
name: material_symbols_icons
|
||||||
sha256: ef20d86fb34c2b59eb7553c4d795bb8a7ec8c890c53ffd3148c64f7adc46ae50
|
sha256: b1342194e859b2774f920b484c46f54a37a845488e23d570385fbe3ede92ee9f
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2858.1"
|
version: "4.2867.0"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1841,10 +1849,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
|
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.5"
|
version: "6.1.5+1"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1897,42 +1905,42 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: record
|
name: record
|
||||||
sha256: "3d08502b77edf2a864aa6e4cd7874b983d42a80f3689431da053cc5e85c1ad21"
|
sha256: "9dbc6ff3e784612f90a9b001373c45ff76b7a08abd2bd9fdf72c242320c8911c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.1.1"
|
||||||
record_android:
|
record_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_android
|
name: record_android
|
||||||
sha256: "8b170e33d9866f9b51e01a767d7e1ecb97b9ecd629950bd87a47c79359ec57f8"
|
sha256: "8361a791c9a3fa5c065f0b8b5adb10f12531f8538c86b19474cf7b56ea80d426"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.1"
|
||||||
record_ios:
|
record_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_ios
|
name: record_ios
|
||||||
sha256: ad97d0a75933c44bcf5aff648e86e32fc05eb61f8fbef190f14968c8eaf86692
|
sha256: "895c9467faec72d8e718a3142b51114958f42f18053836a8b484a74f9372f51a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
record_linux:
|
record_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_linux
|
name: record_linux
|
||||||
sha256: "785e8e8d6db109aa606d0669d95aaae416458aaa39782bb0abe0bee74eee17d7"
|
sha256: "235b1f1fb84e810f8149cc0c2c731d7d697f8d1c333b32cb820c449bf7bb72d8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
record_macos:
|
record_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_macos
|
name: record_macos
|
||||||
sha256: f1399bca76a1634da109e5b0cba764ed8332a2b4da49c704c66d2c553405ed81
|
sha256: "2849068bb59072f300ad63ed146e543d66afaef8263edba4de4834fc7c8d4d35"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
record_platform_interface:
|
record_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1953,10 +1961,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_windows
|
name: record_windows
|
||||||
sha256: "85a22fc97f6d73ecd67c8ba5f2f472b74ef1d906f795b7970f771a0914167e99"
|
sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "1.0.7"
|
||||||
relative_time:
|
relative_time:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
11
pubspec.yaml
11
pubspec.yaml
@@ -36,10 +36,10 @@ dependencies:
|
|||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
flutter_hooks: ^0.21.3
|
flutter_hooks: ^0.21.3+1
|
||||||
hooks_riverpod: ^2.6.1
|
hooks_riverpod: ^2.6.1
|
||||||
bitsdojo_window: ^0.1.6
|
bitsdojo_window: ^0.1.6
|
||||||
go_router: ^16.1.0
|
go_router: ^16.2.0
|
||||||
styled_widget: ^0.4.1
|
styled_widget: ^0.4.1
|
||||||
shared_preferences: ^2.5.3
|
shared_preferences: ^2.5.3
|
||||||
flutter_riverpod: ^2.6.1
|
flutter_riverpod: ^2.6.1
|
||||||
@@ -73,7 +73,7 @@ dependencies:
|
|||||||
git: https://github.com/LittleSheep2Code/tus_client.git
|
git: https://github.com/LittleSheep2Code/tus_client.git
|
||||||
cross_file: ^0.3.4+2
|
cross_file: ^0.3.4+2
|
||||||
image_picker: ^1.2.0
|
image_picker: ^1.2.0
|
||||||
file_picker: ^10.3.1
|
file_picker: ^10.3.2
|
||||||
riverpod_annotation: ^2.6.1
|
riverpod_annotation: ^2.6.1
|
||||||
image_picker_platform_interface: ^2.11.0
|
image_picker_platform_interface: ^2.11.0
|
||||||
image_picker_android: ^0.8.13
|
image_picker_android: ^0.8.13
|
||||||
@@ -83,7 +83,7 @@ dependencies:
|
|||||||
flutter_udid: ^4.0.0
|
flutter_udid: ^4.0.0
|
||||||
firebase_core: ^4.0.0
|
firebase_core: ^4.0.0
|
||||||
web_socket_channel: ^3.0.3
|
web_socket_channel: ^3.0.3
|
||||||
material_symbols_icons: ^4.2858.1
|
material_symbols_icons: ^4.2867.0
|
||||||
drift: ^2.28.1
|
drift: ^2.28.1
|
||||||
drift_flutter: ^0.2.5
|
drift_flutter: ^0.2.5
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
@@ -107,7 +107,7 @@ dependencies:
|
|||||||
livekit_client: ^2.5.0+hotfix.1
|
livekit_client: ^2.5.0+hotfix.1
|
||||||
pasteboard: ^0.4.0
|
pasteboard: ^0.4.0
|
||||||
flutter_colorpicker: ^1.1.0
|
flutter_colorpicker: ^1.1.0
|
||||||
record: ^6.1.0
|
record: ^6.1.1
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
flutter_otp_text_field: ^1.5.1+1
|
flutter_otp_text_field: ^1.5.1+1
|
||||||
palette_generator: ^0.3.3+7
|
palette_generator: ^0.3.3+7
|
||||||
@@ -138,6 +138,7 @@ dependencies:
|
|||||||
firebase_analytics: ^12.0.0
|
firebase_analytics: ^12.0.0
|
||||||
material_color_utilities: ^0.11.1
|
material_color_utilities: ^0.11.1
|
||||||
screenshot: ^3.0.0
|
screenshot: ^3.0.0
|
||||||
|
flutter_card_swiper: ^7.0.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user