diff --git a/lib/models/heatmap.dart b/lib/models/heatmap.dart index 7b99751a..a1f494b7 100644 --- a/lib/models/heatmap.dart +++ b/lib/models/heatmap.dart @@ -4,23 +4,23 @@ part 'heatmap.freezed.dart'; part 'heatmap.g.dart'; @freezed -sealed class SnHeatmap with _$SnPublisherHeatmap { +sealed class SnHeatmap with _$SnHeatmap { const factory SnHeatmap({ required String unit, @JsonKey(name: 'period_start') required DateTime periodStart, @JsonKey(name: 'period_end') required DateTime periodEnd, required List items, - }) = _SnPublisherHeatmap; + }) = _SnHeatmap; factory SnHeatmap.fromJson(Map json) => - _$SnPublisherHeatmapFromJson(json); + _$SnHeatmapFromJson(json); } @freezed -sealed class SnHeatmapItem with _$SnPublisherHeatmapItem { +sealed class SnHeatmapItem with _$SnHeatmapItem { const factory SnHeatmapItem({required DateTime date, required int count}) = - _SnPublisherHeatmapItem; + _SnHeatmapItem; factory SnHeatmapItem.fromJson(Map json) => - _$SnPublisherHeatmapItemFromJson(json); + _$SnHeatmapItemFromJson(json); } diff --git a/lib/models/heatmap.freezed.dart b/lib/models/heatmap.freezed.dart index 30b6f8f1..c0523277 100644 --- a/lib/models/heatmap.freezed.dart +++ b/lib/models/heatmap.freezed.dart @@ -13,16 +13,16 @@ part of 'heatmap.dart'; T _$identity(T value) => value; /// @nodoc -mixin _$SnPublisherHeatmap { +mixin _$SnHeatmap { String get unit;@JsonKey(name: 'period_start') DateTime get periodStart;@JsonKey(name: 'period_end') DateTime get periodEnd; List get items; -/// Create a copy of SnPublisherHeatmap +/// Create a copy of SnHeatmap /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SnPublisherHeatmapCopyWith get copyWith => _$SnPublisherHeatmapCopyWithImpl(this as SnHeatmap, _$identity); +$SnHeatmapCopyWith get copyWith => _$SnHeatmapCopyWithImpl(this as SnHeatmap, _$identity); - /// Serializes this SnPublisherHeatmap to a JSON map. + /// Serializes this SnHeatmap to a JSON map. Map toJson(); @@ -37,15 +37,15 @@ int get hashCode => Object.hash(runtimeType,unit,periodStart,periodEnd,const Dee @override String toString() { - return 'SnPublisherHeatmap(unit: $unit, periodStart: $periodStart, periodEnd: $periodEnd, items: $items)'; + return 'SnHeatmap(unit: $unit, periodStart: $periodStart, periodEnd: $periodEnd, items: $items)'; } } /// @nodoc -abstract mixin class $SnPublisherHeatmapCopyWith<$Res> { - factory $SnPublisherHeatmapCopyWith(SnHeatmap value, $Res Function(SnHeatmap) _then) = _$SnPublisherHeatmapCopyWithImpl; +abstract mixin class $SnHeatmapCopyWith<$Res> { + factory $SnHeatmapCopyWith(SnHeatmap value, $Res Function(SnHeatmap) _then) = _$SnHeatmapCopyWithImpl; @useResult $Res call({ String unit,@JsonKey(name: 'period_start') DateTime periodStart,@JsonKey(name: 'period_end') DateTime periodEnd, List items @@ -56,14 +56,14 @@ $Res call({ } /// @nodoc -class _$SnPublisherHeatmapCopyWithImpl<$Res> - implements $SnPublisherHeatmapCopyWith<$Res> { - _$SnPublisherHeatmapCopyWithImpl(this._self, this._then); +class _$SnHeatmapCopyWithImpl<$Res> + implements $SnHeatmapCopyWith<$Res> { + _$SnHeatmapCopyWithImpl(this._self, this._then); final SnHeatmap _self; final $Res Function(SnHeatmap) _then; -/// Create a copy of SnPublisherHeatmap +/// Create a copy of SnHeatmap /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({Object? unit = null,Object? periodStart = null,Object? periodEnd = null,Object? items = null,}) { return _then(_self.copyWith( @@ -79,7 +79,7 @@ as List, /// Adds pattern-matching-related methods to [SnHeatmap]. -extension SnPublisherHeatmapPatterns on SnHeatmap { +extension SnHeatmapPatterns on SnHeatmap { /// A variant of `map` that fallback to returning `orElse`. /// /// It is equivalent to doing: @@ -92,10 +92,10 @@ extension SnPublisherHeatmapPatterns on SnHeatmap { /// } /// ``` -@optionalTypeArgs TResult maybeMap(TResult Function( _SnPublisherHeatmap value)? $default,{required TResult orElse(),}){ +@optionalTypeArgs TResult maybeMap(TResult Function( _SnHeatmap value)? $default,{required TResult orElse(),}){ final _that = this; switch (_that) { -case _SnPublisherHeatmap() when $default != null: +case _SnHeatmap() when $default != null: return $default(_that);case _: return orElse(); @@ -114,10 +114,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult map(TResult Function( _SnPublisherHeatmap value) $default,){ +@optionalTypeArgs TResult map(TResult Function( _SnHeatmap value) $default,){ final _that = this; switch (_that) { -case _SnPublisherHeatmap(): +case _SnHeatmap(): return $default(_that);} } /// A variant of `map` that fallback to returning `null`. @@ -132,10 +132,10 @@ return $default(_that);} /// } /// ``` -@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnPublisherHeatmap value)? $default,){ +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnHeatmap value)? $default,){ final _that = this; switch (_that) { -case _SnPublisherHeatmap() when $default != null: +case _SnHeatmap() when $default != null: return $default(_that);case _: return null; @@ -155,7 +155,7 @@ return $default(_that);case _: @optionalTypeArgs TResult maybeWhen(TResult Function( String unit, @JsonKey(name: 'period_start') DateTime periodStart, @JsonKey(name: 'period_end') DateTime periodEnd, List items)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { -case _SnPublisherHeatmap() when $default != null: +case _SnHeatmap() when $default != null: return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);case _: return orElse(); @@ -176,7 +176,7 @@ return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);case _ @optionalTypeArgs TResult when(TResult Function( String unit, @JsonKey(name: 'period_start') DateTime periodStart, @JsonKey(name: 'period_end') DateTime periodEnd, List items) $default,) {final _that = this; switch (_that) { -case _SnPublisherHeatmap(): +case _SnHeatmap(): return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);} } /// A variant of `when` that fallback to returning `null` @@ -193,7 +193,7 @@ return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);} @optionalTypeArgs TResult? whenOrNull(TResult? Function( String unit, @JsonKey(name: 'period_start') DateTime periodStart, @JsonKey(name: 'period_end') DateTime periodEnd, List items)? $default,) {final _that = this; switch (_that) { -case _SnPublisherHeatmap() when $default != null: +case _SnHeatmap() when $default != null: return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);case _: return null; @@ -205,9 +205,9 @@ return $default(_that.unit,_that.periodStart,_that.periodEnd,_that.items);case _ /// @nodoc @JsonSerializable() -class _SnPublisherHeatmap implements SnHeatmap { - const _SnPublisherHeatmap({required this.unit, @JsonKey(name: 'period_start') required this.periodStart, @JsonKey(name: 'period_end') required this.periodEnd, required final List items}): _items = items; - factory _SnPublisherHeatmap.fromJson(Map json) => _$SnPublisherHeatmapFromJson(json); +class _SnHeatmap implements SnHeatmap { + const _SnHeatmap({required this.unit, @JsonKey(name: 'period_start') required this.periodStart, @JsonKey(name: 'period_end') required this.periodEnd, required final List items}): _items = items; + factory _SnHeatmap.fromJson(Map json) => _$SnHeatmapFromJson(json); @override final String unit; @override@JsonKey(name: 'period_start') final DateTime periodStart; @@ -220,20 +220,20 @@ class _SnPublisherHeatmap implements SnHeatmap { } -/// Create a copy of SnPublisherHeatmap +/// Create a copy of SnHeatmap /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -_$SnPublisherHeatmapCopyWith<_SnPublisherHeatmap> get copyWith => __$SnPublisherHeatmapCopyWithImpl<_SnPublisherHeatmap>(this, _$identity); +_$SnHeatmapCopyWith<_SnHeatmap> get copyWith => __$SnHeatmapCopyWithImpl<_SnHeatmap>(this, _$identity); @override Map toJson() { - return _$SnPublisherHeatmapToJson(this, ); + return _$SnHeatmapToJson(this, ); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisherHeatmap&&(identical(other.unit, unit) || other.unit == unit)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&const DeepCollectionEquality().equals(other._items, _items)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnHeatmap&&(identical(other.unit, unit) || other.unit == unit)&&(identical(other.periodStart, periodStart) || other.periodStart == periodStart)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&const DeepCollectionEquality().equals(other._items, _items)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -242,15 +242,15 @@ int get hashCode => Object.hash(runtimeType,unit,periodStart,periodEnd,const Dee @override String toString() { - return 'SnPublisherHeatmap(unit: $unit, periodStart: $periodStart, periodEnd: $periodEnd, items: $items)'; + return 'SnHeatmap(unit: $unit, periodStart: $periodStart, periodEnd: $periodEnd, items: $items)'; } } /// @nodoc -abstract mixin class _$SnPublisherHeatmapCopyWith<$Res> implements $SnPublisherHeatmapCopyWith<$Res> { - factory _$SnPublisherHeatmapCopyWith(_SnPublisherHeatmap value, $Res Function(_SnPublisherHeatmap) _then) = __$SnPublisherHeatmapCopyWithImpl; +abstract mixin class _$SnHeatmapCopyWith<$Res> implements $SnHeatmapCopyWith<$Res> { + factory _$SnHeatmapCopyWith(_SnHeatmap value, $Res Function(_SnHeatmap) _then) = __$SnHeatmapCopyWithImpl; @override @useResult $Res call({ String unit,@JsonKey(name: 'period_start') DateTime periodStart,@JsonKey(name: 'period_end') DateTime periodEnd, List items @@ -261,17 +261,17 @@ $Res call({ } /// @nodoc -class __$SnPublisherHeatmapCopyWithImpl<$Res> - implements _$SnPublisherHeatmapCopyWith<$Res> { - __$SnPublisherHeatmapCopyWithImpl(this._self, this._then); +class __$SnHeatmapCopyWithImpl<$Res> + implements _$SnHeatmapCopyWith<$Res> { + __$SnHeatmapCopyWithImpl(this._self, this._then); - final _SnPublisherHeatmap _self; - final $Res Function(_SnPublisherHeatmap) _then; + final _SnHeatmap _self; + final $Res Function(_SnHeatmap) _then; -/// Create a copy of SnPublisherHeatmap +/// Create a copy of SnHeatmap /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? unit = null,Object? periodStart = null,Object? periodEnd = null,Object? items = null,}) { - return _then(_SnPublisherHeatmap( + return _then(_SnHeatmap( unit: null == unit ? _self.unit : unit // ignore: cast_nullable_to_non_nullable as String,periodStart: null == periodStart ? _self.periodStart : periodStart // ignore: cast_nullable_to_non_nullable as DateTime,periodEnd: null == periodEnd ? _self.periodEnd : periodEnd // ignore: cast_nullable_to_non_nullable @@ -285,16 +285,16 @@ as List, /// @nodoc -mixin _$SnPublisherHeatmapItem { +mixin _$SnHeatmapItem { DateTime get date; int get count; -/// Create a copy of SnPublisherHeatmapItem +/// Create a copy of SnHeatmapItem /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$SnPublisherHeatmapItemCopyWith get copyWith => _$SnPublisherHeatmapItemCopyWithImpl(this as SnHeatmapItem, _$identity); +$SnHeatmapItemCopyWith get copyWith => _$SnHeatmapItemCopyWithImpl(this as SnHeatmapItem, _$identity); - /// Serializes this SnPublisherHeatmapItem to a JSON map. + /// Serializes this SnHeatmapItem to a JSON map. Map toJson(); @@ -309,15 +309,15 @@ int get hashCode => Object.hash(runtimeType,date,count); @override String toString() { - return 'SnPublisherHeatmapItem(date: $date, count: $count)'; + return 'SnHeatmapItem(date: $date, count: $count)'; } } /// @nodoc -abstract mixin class $SnPublisherHeatmapItemCopyWith<$Res> { - factory $SnPublisherHeatmapItemCopyWith(SnHeatmapItem value, $Res Function(SnHeatmapItem) _then) = _$SnPublisherHeatmapItemCopyWithImpl; +abstract mixin class $SnHeatmapItemCopyWith<$Res> { + factory $SnHeatmapItemCopyWith(SnHeatmapItem value, $Res Function(SnHeatmapItem) _then) = _$SnHeatmapItemCopyWithImpl; @useResult $Res call({ DateTime date, int count @@ -328,14 +328,14 @@ $Res call({ } /// @nodoc -class _$SnPublisherHeatmapItemCopyWithImpl<$Res> - implements $SnPublisherHeatmapItemCopyWith<$Res> { - _$SnPublisherHeatmapItemCopyWithImpl(this._self, this._then); +class _$SnHeatmapItemCopyWithImpl<$Res> + implements $SnHeatmapItemCopyWith<$Res> { + _$SnHeatmapItemCopyWithImpl(this._self, this._then); final SnHeatmapItem _self; final $Res Function(SnHeatmapItem) _then; -/// Create a copy of SnPublisherHeatmapItem +/// Create a copy of SnHeatmapItem /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({Object? date = null,Object? count = null,}) { return _then(_self.copyWith( @@ -349,7 +349,7 @@ as int, /// Adds pattern-matching-related methods to [SnHeatmapItem]. -extension SnPublisherHeatmapItemPatterns on SnHeatmapItem { +extension SnHeatmapItemPatterns on SnHeatmapItem { /// A variant of `map` that fallback to returning `orElse`. /// /// It is equivalent to doing: @@ -362,10 +362,10 @@ extension SnPublisherHeatmapItemPatterns on SnHeatmapItem { /// } /// ``` -@optionalTypeArgs TResult maybeMap(TResult Function( _SnPublisherHeatmapItem value)? $default,{required TResult orElse(),}){ +@optionalTypeArgs TResult maybeMap(TResult Function( _SnHeatmapItem value)? $default,{required TResult orElse(),}){ final _that = this; switch (_that) { -case _SnPublisherHeatmapItem() when $default != null: +case _SnHeatmapItem() when $default != null: return $default(_that);case _: return orElse(); @@ -384,10 +384,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult map(TResult Function( _SnPublisherHeatmapItem value) $default,){ +@optionalTypeArgs TResult map(TResult Function( _SnHeatmapItem value) $default,){ final _that = this; switch (_that) { -case _SnPublisherHeatmapItem(): +case _SnHeatmapItem(): return $default(_that);} } /// A variant of `map` that fallback to returning `null`. @@ -402,10 +402,10 @@ return $default(_that);} /// } /// ``` -@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnPublisherHeatmapItem value)? $default,){ +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnHeatmapItem value)? $default,){ final _that = this; switch (_that) { -case _SnPublisherHeatmapItem() when $default != null: +case _SnHeatmapItem() when $default != null: return $default(_that);case _: return null; @@ -425,7 +425,7 @@ return $default(_that);case _: @optionalTypeArgs TResult maybeWhen(TResult Function( DateTime date, int count)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { -case _SnPublisherHeatmapItem() when $default != null: +case _SnHeatmapItem() when $default != null: return $default(_that.date,_that.count);case _: return orElse(); @@ -446,7 +446,7 @@ return $default(_that.date,_that.count);case _: @optionalTypeArgs TResult when(TResult Function( DateTime date, int count) $default,) {final _that = this; switch (_that) { -case _SnPublisherHeatmapItem(): +case _SnHeatmapItem(): return $default(_that.date,_that.count);} } /// A variant of `when` that fallback to returning `null` @@ -463,7 +463,7 @@ return $default(_that.date,_that.count);} @optionalTypeArgs TResult? whenOrNull(TResult? Function( DateTime date, int count)? $default,) {final _that = this; switch (_that) { -case _SnPublisherHeatmapItem() when $default != null: +case _SnHeatmapItem() when $default != null: return $default(_that.date,_that.count);case _: return null; @@ -475,27 +475,27 @@ return $default(_that.date,_that.count);case _: /// @nodoc @JsonSerializable() -class _SnPublisherHeatmapItem implements SnHeatmapItem { - const _SnPublisherHeatmapItem({required this.date, required this.count}); - factory _SnPublisherHeatmapItem.fromJson(Map json) => _$SnPublisherHeatmapItemFromJson(json); +class _SnHeatmapItem implements SnHeatmapItem { + const _SnHeatmapItem({required this.date, required this.count}); + factory _SnHeatmapItem.fromJson(Map json) => _$SnHeatmapItemFromJson(json); @override final DateTime date; @override final int count; -/// Create a copy of SnPublisherHeatmapItem +/// Create a copy of SnHeatmapItem /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -_$SnPublisherHeatmapItemCopyWith<_SnPublisherHeatmapItem> get copyWith => __$SnPublisherHeatmapItemCopyWithImpl<_SnPublisherHeatmapItem>(this, _$identity); +_$SnHeatmapItemCopyWith<_SnHeatmapItem> get copyWith => __$SnHeatmapItemCopyWithImpl<_SnHeatmapItem>(this, _$identity); @override Map toJson() { - return _$SnPublisherHeatmapItemToJson(this, ); + return _$SnHeatmapItemToJson(this, ); } @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisherHeatmapItem&&(identical(other.date, date) || other.date == date)&&(identical(other.count, count) || other.count == count)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnHeatmapItem&&(identical(other.date, date) || other.date == date)&&(identical(other.count, count) || other.count == count)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -504,15 +504,15 @@ int get hashCode => Object.hash(runtimeType,date,count); @override String toString() { - return 'SnPublisherHeatmapItem(date: $date, count: $count)'; + return 'SnHeatmapItem(date: $date, count: $count)'; } } /// @nodoc -abstract mixin class _$SnPublisherHeatmapItemCopyWith<$Res> implements $SnPublisherHeatmapItemCopyWith<$Res> { - factory _$SnPublisherHeatmapItemCopyWith(_SnPublisherHeatmapItem value, $Res Function(_SnPublisherHeatmapItem) _then) = __$SnPublisherHeatmapItemCopyWithImpl; +abstract mixin class _$SnHeatmapItemCopyWith<$Res> implements $SnHeatmapItemCopyWith<$Res> { + factory _$SnHeatmapItemCopyWith(_SnHeatmapItem value, $Res Function(_SnHeatmapItem) _then) = __$SnHeatmapItemCopyWithImpl; @override @useResult $Res call({ DateTime date, int count @@ -523,17 +523,17 @@ $Res call({ } /// @nodoc -class __$SnPublisherHeatmapItemCopyWithImpl<$Res> - implements _$SnPublisherHeatmapItemCopyWith<$Res> { - __$SnPublisherHeatmapItemCopyWithImpl(this._self, this._then); +class __$SnHeatmapItemCopyWithImpl<$Res> + implements _$SnHeatmapItemCopyWith<$Res> { + __$SnHeatmapItemCopyWithImpl(this._self, this._then); - final _SnPublisherHeatmapItem _self; - final $Res Function(_SnPublisherHeatmapItem) _then; + final _SnHeatmapItem _self; + final $Res Function(_SnHeatmapItem) _then; -/// Create a copy of SnPublisherHeatmapItem +/// Create a copy of SnHeatmapItem /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? date = null,Object? count = null,}) { - return _then(_SnPublisherHeatmapItem( + return _then(_SnHeatmapItem( date: null == date ? _self.date : date // ignore: cast_nullable_to_non_nullable as DateTime,count: null == count ? _self.count : count // ignore: cast_nullable_to_non_nullable as int, diff --git a/lib/models/heatmap.g.dart b/lib/models/heatmap.g.dart index 9bec7fd3..5c17236c 100644 --- a/lib/models/heatmap.g.dart +++ b/lib/models/heatmap.g.dart @@ -6,18 +6,17 @@ part of 'heatmap.dart'; // JsonSerializableGenerator // ************************************************************************** -_SnPublisherHeatmap _$SnPublisherHeatmapFromJson(Map json) => - _SnPublisherHeatmap( - unit: json['unit'] as String, - periodStart: DateTime.parse(json['period_start'] as String), - periodEnd: DateTime.parse(json['period_end'] as String), - items: - (json['items'] as List) - .map((e) => SnHeatmapItem.fromJson(e as Map)) - .toList(), - ); +_SnHeatmap _$SnHeatmapFromJson(Map json) => _SnHeatmap( + unit: json['unit'] as String, + periodStart: DateTime.parse(json['period_start'] as String), + periodEnd: DateTime.parse(json['period_end'] as String), + items: + (json['items'] as List) + .map((e) => SnHeatmapItem.fromJson(e as Map)) + .toList(), +); -Map _$SnPublisherHeatmapToJson(_SnPublisherHeatmap instance) => +Map _$SnHeatmapToJson(_SnHeatmap instance) => { 'unit': instance.unit, 'period_start': instance.periodStart.toIso8601String(), @@ -25,16 +24,14 @@ Map _$SnPublisherHeatmapToJson(_SnPublisherHeatmap instance) => 'items': instance.items.map((e) => e.toJson()).toList(), }; -_SnPublisherHeatmapItem _$SnPublisherHeatmapItemFromJson( - Map json, -) => _SnPublisherHeatmapItem( - date: DateTime.parse(json['date'] as String), - count: (json['count'] as num).toInt(), -); +_SnHeatmapItem _$SnHeatmapItemFromJson(Map json) => + _SnHeatmapItem( + date: DateTime.parse(json['date'] as String), + count: (json['count'] as num).toInt(), + ); -Map _$SnPublisherHeatmapItemToJson( - _SnPublisherHeatmapItem instance, -) => { - 'date': instance.date.toIso8601String(), - 'count': instance.count, -}; +Map _$SnHeatmapItemToJson(_SnHeatmapItem instance) => + { + 'date': instance.date.toIso8601String(), + 'count': instance.count, + }; diff --git a/lib/models/post.dart b/lib/models/post.dart index 04b17785..9a202fbc 100644 --- a/lib/models/post.dart +++ b/lib/models/post.dart @@ -1,4 +1,5 @@ import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:island/models/account.dart'; import 'package:island/models/file.dart'; import 'package:island/models/post_category.dart'; import 'package:island/models/post_tag.dart'; @@ -142,3 +143,21 @@ sealed class SnPostAward with _$SnPostAward { factory SnPostAward.fromJson(Map json) => _$SnPostAwardFromJson(json); } + +@freezed +sealed class SnPostReaction with _$SnPostReaction { + const factory SnPostReaction({ + required String id, + required String symbol, + required int attitude, + required String postId, + required String accountId, + required DateTime createdAt, + required DateTime updatedAt, + @Default(null) SnAccount? account, + DateTime? deletedAt, + }) = _SnPostReaction; + + factory SnPostReaction.fromJson(Map json) => + _$SnPostReactionFromJson(json); +} diff --git a/lib/models/post.freezed.dart b/lib/models/post.freezed.dart index dfc24d43..085221c3 100644 --- a/lib/models/post.freezed.dart +++ b/lib/models/post.freezed.dart @@ -1900,4 +1900,309 @@ as DateTime?, } + +/// @nodoc +mixin _$SnPostReaction { + + String get id; String get symbol; int get attitude; String get postId; String get accountId; DateTime get createdAt; DateTime get updatedAt; SnAccount? get account; DateTime? get deletedAt; +/// Create a copy of SnPostReaction +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$SnPostReactionCopyWith get copyWith => _$SnPostReactionCopyWithImpl(this as SnPostReaction, _$identity); + + /// Serializes this SnPostReaction to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,accountId,createdAt,updatedAt,account,deletedAt); + +@override +String toString() { + return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account, deletedAt: $deletedAt)'; +} + + +} + +/// @nodoc +abstract mixin class $SnPostReactionCopyWith<$Res> { + factory $SnPostReactionCopyWith(SnPostReaction value, $Res Function(SnPostReaction) _then) = _$SnPostReactionCopyWithImpl; +@useResult +$Res call({ + String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt +}); + + +$SnAccountCopyWith<$Res>? get account; + +} +/// @nodoc +class _$SnPostReactionCopyWithImpl<$Res> + implements $SnPostReactionCopyWith<$Res> { + _$SnPostReactionCopyWithImpl(this._self, this._then); + + final SnPostReaction _self; + final $Res Function(SnPostReaction) _then; + +/// Create a copy of SnPostReaction +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = freezed,Object? deletedAt = freezed,}) { + return _then(_self.copyWith( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable +as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable +as int,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable +as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable +as String,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,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable +as SnAccount?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} +/// Create a copy of SnPostReaction +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnAccountCopyWith<$Res>? get account { + if (_self.account == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_self.account!, (value) { + return _then(_self.copyWith(account: value)); + }); +} +} + + +/// Adds pattern-matching-related methods to [SnPostReaction]. +extension SnPostReactionPatterns on SnPostReaction { +/// A variant of `map` that fallback to returning `orElse`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeMap(TResult Function( _SnPostReaction value)? $default,{required TResult orElse(),}){ +final _that = this; +switch (_that) { +case _SnPostReaction() when $default != null: +return $default(_that);case _: + return orElse(); + +} +} +/// A `switch`-like method, using callbacks. +/// +/// Callbacks receives the raw object, upcasted. +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case final Subclass2 value: +/// return ...; +/// } +/// ``` + +@optionalTypeArgs TResult map(TResult Function( _SnPostReaction value) $default,){ +final _that = this; +switch (_that) { +case _SnPostReaction(): +return $default(_that);} +} +/// A variant of `map` that fallback to returning `null`. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case final Subclass value: +/// return ...; +/// case _: +/// return null; +/// } +/// ``` + +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SnPostReaction value)? $default,){ +final _that = this; +switch (_that) { +case _SnPostReaction() when $default != null: +return $default(_that);case _: + return null; + +} +} +/// A variant of `when` that fallback to an `orElse` callback. +/// +/// It is equivalent to doing: +/// ```dart +/// switch (sealedClass) { +/// case Subclass(:final field): +/// return ...; +/// case _: +/// return orElse(); +/// } +/// ``` + +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; +switch (_that) { +case _SnPostReaction() when $default != null: +return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_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 Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt) $default,) {final _that = this; +switch (_that) { +case _SnPostReaction(): +return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_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? Function( String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt)? $default,) {final _that = this; +switch (_that) { +case _SnPostReaction() when $default != null: +return $default(_that.id,_that.symbol,_that.attitude,_that.postId,_that.accountId,_that.createdAt,_that.updatedAt,_that.account,_that.deletedAt);case _: + return null; + +} +} + +} + +/// @nodoc +@JsonSerializable() + +class _SnPostReaction implements SnPostReaction { + const _SnPostReaction({required this.id, required this.symbol, required this.attitude, required this.postId, required this.accountId, required this.createdAt, required this.updatedAt, this.account = null, this.deletedAt}); + factory _SnPostReaction.fromJson(Map json) => _$SnPostReactionFromJson(json); + +@override final String id; +@override final String symbol; +@override final int attitude; +@override final String postId; +@override final String accountId; +@override final DateTime createdAt; +@override final DateTime updatedAt; +@override@JsonKey() final SnAccount? account; +@override final DateTime? deletedAt; + +/// Create a copy of SnPostReaction +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$SnPostReactionCopyWith<_SnPostReaction> get copyWith => __$SnPostReactionCopyWithImpl<_SnPostReaction>(this, _$identity); + +@override +Map toJson() { + return _$SnPostReactionToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPostReaction&&(identical(other.id, id) || other.id == id)&&(identical(other.symbol, symbol) || other.symbol == symbol)&&(identical(other.attitude, attitude) || other.attitude == attitude)&&(identical(other.postId, postId) || other.postId == postId)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.account, account) || other.account == account)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,id,symbol,attitude,postId,accountId,createdAt,updatedAt,account,deletedAt); + +@override +String toString() { + return 'SnPostReaction(id: $id, symbol: $symbol, attitude: $attitude, postId: $postId, accountId: $accountId, createdAt: $createdAt, updatedAt: $updatedAt, account: $account, deletedAt: $deletedAt)'; +} + + +} + +/// @nodoc +abstract mixin class _$SnPostReactionCopyWith<$Res> implements $SnPostReactionCopyWith<$Res> { + factory _$SnPostReactionCopyWith(_SnPostReaction value, $Res Function(_SnPostReaction) _then) = __$SnPostReactionCopyWithImpl; +@override @useResult +$Res call({ + String id, String symbol, int attitude, String postId, String accountId, DateTime createdAt, DateTime updatedAt, SnAccount? account, DateTime? deletedAt +}); + + +@override $SnAccountCopyWith<$Res>? get account; + +} +/// @nodoc +class __$SnPostReactionCopyWithImpl<$Res> + implements _$SnPostReactionCopyWith<$Res> { + __$SnPostReactionCopyWithImpl(this._self, this._then); + + final _SnPostReaction _self; + final $Res Function(_SnPostReaction) _then; + +/// Create a copy of SnPostReaction +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? symbol = null,Object? attitude = null,Object? postId = null,Object? accountId = null,Object? createdAt = null,Object? updatedAt = null,Object? account = freezed,Object? deletedAt = freezed,}) { + return _then(_SnPostReaction( +id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable +as String,symbol: null == symbol ? _self.symbol : symbol // ignore: cast_nullable_to_non_nullable +as String,attitude: null == attitude ? _self.attitude : attitude // ignore: cast_nullable_to_non_nullable +as int,postId: null == postId ? _self.postId : postId // ignore: cast_nullable_to_non_nullable +as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable +as String,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,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable +as SnAccount?,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable +as DateTime?, + )); +} + +/// Create a copy of SnPostReaction +/// with the given fields replaced by the non-null parameter values. +@override +@pragma('vm:prefer-inline') +$SnAccountCopyWith<$Res>? get account { + if (_self.account == null) { + return null; + } + + return $SnAccountCopyWith<$Res>(_self.account!, (value) { + return _then(_self.copyWith(account: value)); + }); +} +} + // dart format on diff --git a/lib/models/post.g.dart b/lib/models/post.g.dart index 0a17ad3b..556f2684 100644 --- a/lib/models/post.g.dart +++ b/lib/models/post.g.dart @@ -230,3 +230,35 @@ Map _$SnPostAwardToJson(_SnPostAward instance) => 'updated_at': instance.updatedAt?.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), }; + +_SnPostReaction _$SnPostReactionFromJson(Map json) => + _SnPostReaction( + id: json['id'] as String, + symbol: json['symbol'] as String, + attitude: (json['attitude'] as num).toInt(), + postId: json['post_id'] as String, + accountId: json['account_id'] as String, + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + account: + json['account'] == null + ? null + : SnAccount.fromJson(json['account'] as Map), + deletedAt: + json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + ); + +Map _$SnPostReactionToJson(_SnPostReaction instance) => + { + 'id': instance.id, + 'symbol': instance.symbol, + 'attitude': instance.attitude, + 'post_id': instance.postId, + 'account_id': instance.accountId, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'account': instance.account?.toJson(), + 'deleted_at': instance.deletedAt?.toIso8601String(), + }; diff --git a/lib/screens/creators/hub.g.dart b/lib/screens/creators/hub.g.dart index 9fc985dd..3540469f 100644 --- a/lib/screens/creators/hub.g.dart +++ b/lib/screens/creators/hub.g.dart @@ -149,7 +149,7 @@ class _PublisherStatsProviderElement String? get uname => (origin as PublisherStatsProvider).uname; } -String _$publisherHeatmapHash() => r'780dfb05b8610a37cfcd937fd04cf5bbe9b298c9'; +String _$publisherHeatmapHash() => r'5f70c55e14629ec8628445a317888e02fccd9af2'; /// See also [publisherHeatmap]. @ProviderFor(publisherHeatmap) diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index c8785d5d..5359829e 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -579,6 +579,7 @@ class PostItem extends HookConsumerWidget { onReact: (symbol, attitude) { reactPost(symbol, attitude); }, + postId: item.id, ); }, ); @@ -686,6 +687,7 @@ class PostReactionList extends HookConsumerWidget { onReact: (symbol, attitude) { reactPost(symbol, attitude); }, + postId: parentId, ); }, ); diff --git a/lib/widgets/post/post_reaction_sheet.dart b/lib/widgets/post/post_reaction_sheet.dart index a0409cc9..97e933b0 100644 --- a/lib/widgets/post/post_reaction_sheet.dart +++ b/lib/widgets/post/post_reaction_sheet.dart @@ -1,7 +1,15 @@ +import 'dart:math' as math; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_popup_card/flutter_popup_card.dart'; import 'package:gap/gap.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/post.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/services/time.dart'; +import 'package:island/widgets/content/cloud_files.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -39,10 +47,12 @@ class PostReactionSheet extends StatelessWidget { final Map reactionsCount; final Map reactionsMade; final Function(String symbol, int attitude) onReact; + final String postId; const PostReactionSheet({ required this.reactionsCount, required this.reactionsMade, required this.onReact, + required this.postId, }); @override @@ -144,93 +154,105 @@ class PostReactionSheet extends StatelessWidget { final symbol = allReactions[index]; final count = reactionsCount[symbol] ?? 0; final hasImage = _getReactionImageAvailable(symbol); - return Badge( - label: Text('x$count'), - isLabelVisible: count > 0, - textColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: Theme.of(context).colorScheme.primary, - offset: Offset(0, 0), - child: Card( - margin: const EdgeInsets.symmetric(vertical: 4), - color: Theme.of(context).colorScheme.surfaceContainerLowest, - child: InkWell( - borderRadius: const BorderRadius.all(Radius.circular(8)), - onTap: () { - onReact(symbol, attitude); - Navigator.pop(context); - }, - child: Container( - decoration: - hasImage - ? BoxDecoration( - borderRadius: BorderRadius.circular(8), - image: DecorationImage( - image: AssetImage( - 'assets/images/stickers/$symbol.png', + return GestureDetector( + onLongPressStart: (details) { + if (count > 0) { + showReactionDetailsPopup( + context, + symbol, + details.localPosition, + postId, + ); + } + }, + child: Badge( + label: Text('x$count'), + isLabelVisible: count > 0, + textColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: Theme.of(context).colorScheme.primary, + offset: Offset(0, 0), + child: Card( + margin: const EdgeInsets.symmetric(vertical: 4), + color: Theme.of(context).colorScheme.surfaceContainerLowest, + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(8)), + onTap: () { + onReact(symbol, attitude); + Navigator.pop(context); + }, + child: Container( + decoration: + hasImage + ? BoxDecoration( + borderRadius: BorderRadius.circular(8), + image: DecorationImage( + image: AssetImage( + 'assets/images/stickers/$symbol.png', + ), + fit: BoxFit.cover, + colorFilter: + (reactionsMade[symbol] ?? false) + ? ColorFilter.mode( + Theme.of(context) + .colorScheme + .primaryContainer + .withOpacity(0.7), + BlendMode.srcATop, + ) + : null, + ), + ) + : null, + child: Stack( + fit: StackFit.expand, + children: [ + if (hasImage) + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.7), + Colors.transparent, + ], + stops: [0.0, 0.3], ), - fit: BoxFit.cover, - colorFilter: - (reactionsMade[symbol] ?? false) - ? ColorFilter.mode( - Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.7), - BlendMode.srcATop, - ) - : null, - ), - ) - : null, - child: Stack( - fit: StackFit.expand, - children: [ - if (hasImage) - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.7), - Colors.transparent, - ], - stops: [0.0, 0.3], ), ), + Column( + mainAxisAlignment: + hasImage + ? MainAxisAlignment.end + : MainAxisAlignment.center, + children: [ + if (!hasImage) _buildReactionIcon(symbol, 36), + Text( + ReactInfo.getTranslationKey(symbol), + textAlign: TextAlign.center, + style: TextStyle( + color: hasImage ? Colors.white : null, + shadows: + hasImage + ? [ + const Shadow( + blurRadius: 4, + offset: Offset(0.5, 0.5), + color: Colors.black, + ), + ] + : null, + ), + ).tr(), + if (hasImage) const Gap(4), + ], ), - Column( - mainAxisAlignment: - hasImage - ? MainAxisAlignment.end - : MainAxisAlignment.center, - children: [ - if (!hasImage) _buildReactionIcon(symbol, 36), - Text( - ReactInfo.getTranslationKey(symbol), - textAlign: TextAlign.center, - style: TextStyle( - color: hasImage ? Colors.white : null, - shadows: - hasImage - ? [ - const Shadow( - blurRadius: 4, - offset: Offset(0.5, 0.5), - color: Colors.black, - ), - ] - : null, - ), - ).tr(), - if (hasImage) const Gap(4), - ], - ), - ], + ], + ), ), ), ), @@ -243,3 +265,135 @@ class PostReactionSheet extends StatelessWidget { ); } } + +class ReactionDetailsPopup extends HookConsumerWidget { + final String symbol; + final String postId; + const ReactionDetailsPopup({ + super.key, + required this.symbol, + required this.postId, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final reactions = useState>([]); + final isLoading = useState(false); + final hasMore = useState(true); + final offset = useState(0); + + Future loadReactions() async { + if (isLoading.value || !hasMore.value) return; + + isLoading.value = true; + try { + final client = ref.watch(apiClientProvider); + final response = await client.get( + '/sphere/posts/${postId}/reactions', + queryParameters: { + 'symbol': symbol, + 'offset': offset.value, + 'take': 20, + }, + ); + + final newReactions = + (response.data as List) + .map((json) => SnPostReaction.fromJson(json)) + .toList(); + + if (newReactions.length < 20) { + hasMore.value = false; + } + + reactions.value = [...reactions.value, ...newReactions]; + offset.value += newReactions.length; + } catch (err) { + // Handle error + } finally { + isLoading.value = false; + } + } + + useEffect(() { + loadReactions(); + return null; + }, []); + + final width = math.min(MediaQuery.of(context).size.width * 0.8, 480.0); + return PopupCard( + elevation: 8, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)), + child: SizedBox( + width: width, + height: 400, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + _buildReactionIcon(symbol, 24), + const Gap(8), + Text( + ReactInfo.getTranslationKey(symbol), + style: Theme.of(context).textTheme.titleMedium, + ).tr(), + const Spacer(), + Text('${reactions.value.length} reactions'.tr()), + ], + ), + ), + const Divider(height: 1), + Expanded( + child: ListView.builder( + itemCount: reactions.value.length + (hasMore.value ? 1 : 0), + itemBuilder: (context, index) { + if (index == reactions.value.length) { + if (isLoading.value) { + return const Center( + child: Padding( + padding: EdgeInsets.all(16), + child: CircularProgressIndicator(), + ), + ); + } else { + loadReactions(); + return const SizedBox.shrink(); + } + } + + final reaction = reactions.value[index]; + return ListTile( + leading: ProfilePictureWidget( + file: reaction.account?.profile.picture, + ), + title: Text(reaction.account?.nick ?? 'unknown'.tr()), + subtitle: Text( + '${reaction.createdAt.formatRelative(context)} ยท ${reaction.createdAt.formatSystem()}', + ), + ); + }, + ), + ), + ], + ), + ), + ); + } +} + +Future showReactionDetailsPopup( + BuildContext context, + String symbol, + Offset offset, + String postId, +) async { + await showPopupCard( + offset: offset, + context: context, + builder: (context) => ReactionDetailsPopup(symbol: symbol, postId: postId), + alignment: Alignment.center, + dimBackground: true, + ); +}