🐛 Fixes and optimzation on post
♻️ Replace freezed abstract classes with sealed
			
			
This commit is contained in:
		| @@ -5,7 +5,7 @@ part 'activity.freezed.dart'; | |||||||
| part 'activity.g.dart'; | part 'activity.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnActivity with _$SnActivity { | sealed class SnActivity with _$SnActivity { | ||||||
|   const factory SnActivity({ |   const factory SnActivity({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String type, |     required String type, | ||||||
| @@ -24,7 +24,7 @@ abstract class SnActivity with _$SnActivity { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnCheckInResult with _$SnCheckInResult { | sealed class SnCheckInResult with _$SnCheckInResult { | ||||||
|   const factory SnCheckInResult({ |   const factory SnCheckInResult({ | ||||||
|     required String id, |     required String id, | ||||||
|     required int level, |     required int level, | ||||||
| @@ -41,7 +41,7 @@ abstract class SnCheckInResult with _$SnCheckInResult { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnFortuneTip with _$SnFortuneTip { | sealed class SnFortuneTip with _$SnFortuneTip { | ||||||
|   const factory SnFortuneTip({ |   const factory SnFortuneTip({ | ||||||
|     required bool isPositive, |     required bool isPositive, | ||||||
|     required String title, |     required String title, | ||||||
| @@ -53,7 +53,7 @@ abstract class SnFortuneTip with _$SnFortuneTip { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnEventCalendarEntry with _$SnEventCalendarEntry { | sealed class SnEventCalendarEntry with _$SnEventCalendarEntry { | ||||||
|   const factory SnEventCalendarEntry({ |   const factory SnEventCalendarEntry({ | ||||||
|     required DateTime date, |     required DateTime date, | ||||||
|     required SnCheckInResult? checkInResult, |     required SnCheckInResult? checkInResult, | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ part 'auth.freezed.dart'; | |||||||
| part 'auth.g.dart'; | part 'auth.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class AppTokenPair with _$AppTokenPair { | sealed class AppTokenPair with _$AppTokenPair { | ||||||
|   const factory AppTokenPair({ |   const factory AppTokenPair({ | ||||||
|     required String accessToken, |     required String accessToken, | ||||||
|     required String refreshToken, |     required String refreshToken, | ||||||
| @@ -15,7 +15,7 @@ abstract class AppTokenPair with _$AppTokenPair { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnAuthChallenge with _$SnAuthChallenge { | sealed class SnAuthChallenge with _$SnAuthChallenge { | ||||||
|   const factory SnAuthChallenge({ |   const factory SnAuthChallenge({ | ||||||
|     required String id, |     required String id, | ||||||
|     required DateTime expiredAt, |     required DateTime expiredAt, | ||||||
| @@ -38,7 +38,7 @@ abstract class SnAuthChallenge with _$SnAuthChallenge { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnAuthFactor with _$SnAuthFactor { | sealed class SnAuthFactor with _$SnAuthFactor { | ||||||
|   const factory SnAuthFactor({ |   const factory SnAuthFactor({ | ||||||
|     required String id, |     required String id, | ||||||
|     required int type, |     required int type, | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ part 'chat.freezed.dart'; | |||||||
| part 'chat.g.dart'; | part 'chat.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnChatRoom with _$SnChatRoom { | sealed class SnChatRoom with _$SnChatRoom { | ||||||
|   const factory SnChatRoom({ |   const factory SnChatRoom({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String? name, |     required String? name, | ||||||
| @@ -31,7 +31,7 @@ abstract class SnChatRoom with _$SnChatRoom { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnChatMessage with _$SnChatMessage { | sealed class SnChatMessage with _$SnChatMessage { | ||||||
|   const factory SnChatMessage({ |   const factory SnChatMessage({ | ||||||
|     required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
| @@ -56,7 +56,7 @@ abstract class SnChatMessage with _$SnChatMessage { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnChatReaction with _$SnChatReaction { | sealed class SnChatReaction with _$SnChatReaction { | ||||||
|   const factory SnChatReaction({ |   const factory SnChatReaction({ | ||||||
|     required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
| @@ -74,7 +74,7 @@ abstract class SnChatReaction with _$SnChatReaction { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnChatMember with _$SnChatMember { | sealed class SnChatMember with _$SnChatMember { | ||||||
|   const factory SnChatMember({ |   const factory SnChatMember({ | ||||||
|     required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
| @@ -96,7 +96,7 @@ abstract class SnChatMember with _$SnChatMember { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract 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, | ||||||
| @@ -113,7 +113,7 @@ class MessageChangeAction { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class MessageChange with _$MessageChange { | sealed class MessageChange with _$MessageChange { | ||||||
|   const factory MessageChange({ |   const factory MessageChange({ | ||||||
|     required String messageId, |     required String messageId, | ||||||
|     required String action, |     required String action, | ||||||
| @@ -126,7 +126,7 @@ abstract class MessageChange with _$MessageChange { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class MessageSyncResponse with _$MessageSyncResponse { | sealed class MessageSyncResponse with _$MessageSyncResponse { | ||||||
|   const factory MessageSyncResponse({ |   const factory MessageSyncResponse({ | ||||||
|     @Default([]) List<MessageChange> changes, |     @Default([]) List<MessageChange> changes, | ||||||
|     required DateTime currentTimestamp, |     required DateTime currentTimestamp, | ||||||
| @@ -137,7 +137,7 @@ abstract class MessageSyncResponse with _$MessageSyncResponse { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class ChatRealtimeJoinResponse with _$ChatRealtimeJoinResponse { | sealed class ChatRealtimeJoinResponse with _$ChatRealtimeJoinResponse { | ||||||
|   const factory ChatRealtimeJoinResponse({ |   const factory ChatRealtimeJoinResponse({ | ||||||
|     required String token, |     required String token, | ||||||
|     required Map<String, dynamic> config, |     required Map<String, dynamic> config, | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ part 'file.g.dart'; | |||||||
| enum UniversalFileType { image, video, audio, file } | enum UniversalFileType { image, video, audio, file } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class UniversalFile with _$UniversalFile { | sealed class UniversalFile with _$UniversalFile { | ||||||
|   const UniversalFile._(); |   const UniversalFile._(); | ||||||
|  |  | ||||||
|   const factory UniversalFile({ |   const factory UniversalFile({ | ||||||
| @@ -31,7 +31,7 @@ abstract class UniversalFile with _$UniversalFile { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnCloudFile with _$SnCloudFile { | sealed class SnCloudFile with _$SnCloudFile { | ||||||
|   const factory SnCloudFile({ |   const factory SnCloudFile({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String name, |     required String name, | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ part 'post.freezed.dart'; | |||||||
| part 'post.g.dart'; | part 'post.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnPost with _$SnPost { | sealed class SnPost with _$SnPost { | ||||||
|   const factory SnPost({ |   const factory SnPost({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String? title, |     required String? title, | ||||||
| @@ -43,7 +43,7 @@ abstract class SnPost with _$SnPost { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnPublisher with _$SnPublisher { | sealed class SnPublisher with _$SnPublisher { | ||||||
|   const factory SnPublisher({ |   const factory SnPublisher({ | ||||||
|     required String id, |     required String id, | ||||||
|     required int type, |     required int type, | ||||||
| @@ -66,7 +66,7 @@ abstract class SnPublisher with _$SnPublisher { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnPublisherStats with _$SnPublisherStats { | sealed class SnPublisherStats with _$SnPublisherStats { | ||||||
|   const factory SnPublisherStats({ |   const factory SnPublisherStats({ | ||||||
|     required int postsCreated, |     required int postsCreated, | ||||||
|     required int stickerPacksCreated, |     required int stickerPacksCreated, | ||||||
| @@ -80,7 +80,7 @@ abstract class SnPublisherStats with _$SnPublisherStats { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnSubscriptionStatus with _$SnSubscriptionStatus { | sealed class SnSubscriptionStatus with _$SnSubscriptionStatus { | ||||||
|   const factory SnSubscriptionStatus({ |   const factory SnSubscriptionStatus({ | ||||||
|     required bool isSubscribed, |     required bool isSubscribed, | ||||||
|     required int publisherId, |     required int publisherId, | ||||||
| @@ -92,7 +92,7 @@ abstract class SnSubscriptionStatus with _$SnSubscriptionStatus { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class ReactInfo with _$ReactInfo { | sealed class ReactInfo with _$ReactInfo { | ||||||
|   const factory ReactInfo({required String icon, required int attitude}) = |   const factory ReactInfo({required String icon, required int attitude}) = | ||||||
|       _ReactInfo; |       _ReactInfo; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ part 'realm.freezed.dart'; | |||||||
| part 'realm.g.dart'; | part 'realm.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnRealm with _$SnRealm { | sealed class SnRealm with _$SnRealm { | ||||||
|   const factory SnRealm({ |   const factory SnRealm({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String slug, |     required String slug, | ||||||
| @@ -31,7 +31,7 @@ abstract class SnRealm with _$SnRealm { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnRealmMember with _$SnRealmMember { | sealed class SnRealmMember with _$SnRealmMember { | ||||||
|   const factory SnRealmMember({ |   const factory SnRealmMember({ | ||||||
|     required String realmId, |     required String realmId, | ||||||
|     required SnRealm? realm, |     required SnRealm? realm, | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ part 'relationship.freezed.dart'; | |||||||
| part 'relationship.g.dart'; | part 'relationship.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnRelationship with _$SnRelationship { | sealed class SnRelationship with _$SnRelationship { | ||||||
|   const factory SnRelationship({ |   const factory SnRelationship({ | ||||||
|     required DateTime? createdAt, |     required DateTime? createdAt, | ||||||
|     required DateTime? updatedAt, |     required DateTime? updatedAt, | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ part 'sticker.freezed.dart'; | |||||||
| part 'sticker.g.dart'; | part 'sticker.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnSticker with _$SnSticker { | sealed class SnSticker with _$SnSticker { | ||||||
|   const factory SnSticker({ |   const factory SnSticker({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String slug, |     required String slug, | ||||||
| @@ -24,7 +24,7 @@ abstract class SnSticker with _$SnSticker { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnStickerPack with _$SnStickerPack { | sealed class SnStickerPack with _$SnStickerPack { | ||||||
|   const factory SnStickerPack({ |   const factory SnStickerPack({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String name, |     required String name, | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ part 'user.freezed.dart'; | |||||||
| part 'user.g.dart'; | part 'user.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnAccount with _$SnAccount { | sealed class SnAccount with _$SnAccount { | ||||||
|   const factory SnAccount({ |   const factory SnAccount({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String name, |     required String name, | ||||||
| @@ -24,7 +24,7 @@ abstract class SnAccount with _$SnAccount { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnAccountProfile with _$SnAccountProfile { | sealed class SnAccountProfile with _$SnAccountProfile { | ||||||
|   const factory SnAccountProfile({ |   const factory SnAccountProfile({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String? firstName, |     required String? firstName, | ||||||
| @@ -48,7 +48,7 @@ abstract class SnAccountProfile with _$SnAccountProfile { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnAccountStatus with _$SnAccountStatus { | sealed class SnAccountStatus with _$SnAccountStatus { | ||||||
|   const factory SnAccountStatus({ |   const factory SnAccountStatus({ | ||||||
|     required String id, |     required String id, | ||||||
|     required int attitude, |     required int attitude, | ||||||
| @@ -69,7 +69,7 @@ abstract class SnAccountStatus with _$SnAccountStatus { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnAccountBadge with _$SnAccountBadge { | sealed class SnAccountBadge with _$SnAccountBadge { | ||||||
|   const factory SnAccountBadge({ |   const factory SnAccountBadge({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String type, |     required String type, | ||||||
| @@ -88,7 +88,7 @@ abstract class SnAccountBadge with _$SnAccountBadge { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnNotification with _$SnNotification { | sealed class SnNotification with _$SnNotification { | ||||||
|   const factory SnNotification({ |   const factory SnNotification({ | ||||||
|     required DateTime createdAt, |     required DateTime createdAt, | ||||||
|     required DateTime updatedAt, |     required DateTime updatedAt, | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ part 'wallet.freezed.dart'; | |||||||
| part 'wallet.g.dart'; | part 'wallet.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnWallet with _$SnWallet { | sealed class SnWallet with _$SnWallet { | ||||||
|   const factory SnWallet({ |   const factory SnWallet({ | ||||||
|     required String id, |     required String id, | ||||||
|     required List<SnWalletPocket> pockets, |     required List<SnWalletPocket> pockets, | ||||||
| @@ -21,7 +21,7 @@ abstract class SnWallet with _$SnWallet { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnWalletPocket with _$SnWalletPocket { | sealed class SnWalletPocket with _$SnWalletPocket { | ||||||
|   const factory SnWalletPocket({ |   const factory SnWalletPocket({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String currency, |     required String currency, | ||||||
| @@ -37,7 +37,7 @@ abstract class SnWalletPocket with _$SnWalletPocket { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class SnTransaction with _$SnTransaction { | sealed class SnTransaction with _$SnTransaction { | ||||||
|   const factory SnTransaction({ |   const factory SnTransaction({ | ||||||
|     required String id, |     required String id, | ||||||
|     required String currency, |     required String currency, | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ final serverUrlProvider = Provider<String>((ref) { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class AppSettings with _$AppSettings { | sealed class AppSettings with _$AppSettings { | ||||||
|   const factory AppSettings({ |   const factory AppSettings({ | ||||||
|     required bool autoTranslate, |     required bool autoTranslate, | ||||||
|     required bool soundEffects, |     required bool soundEffects, | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ part 'websocket.freezed.dart'; | |||||||
| part 'websocket.g.dart'; | part 'websocket.g.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class WebSocketState with _$WebSocketState { | sealed class WebSocketState with _$WebSocketState { | ||||||
|   const factory WebSocketState.connected() = _Connected; |   const factory WebSocketState.connected() = _Connected; | ||||||
|   const factory WebSocketState.connecting() = _Connecting; |   const factory WebSocketState.connecting() = _Connecting; | ||||||
|   const factory WebSocketState.disconnected() = _Disconnected; |   const factory WebSocketState.disconnected() = _Disconnected; | ||||||
| @@ -22,7 +22,7 @@ abstract class WebSocketState with _$WebSocketState { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class WebSocketPacket with _$WebSocketPacket { | sealed class WebSocketPacket with _$WebSocketPacket { | ||||||
|   const factory WebSocketPacket({ |   const factory WebSocketPacket({ | ||||||
|     required String type, |     required String type, | ||||||
|     required Map<String, dynamic>? data, |     required Map<String, dynamic>? data, | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ part 'event_calendar.g.dart'; | |||||||
| part 'event_calendar.freezed.dart'; | part 'event_calendar.freezed.dart'; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class EventCalendarQuery with _$EventCalendarQuery { | sealed class EventCalendarQuery with _$EventCalendarQuery { | ||||||
|   const factory EventCalendarQuery({ |   const factory EventCalendarQuery({ | ||||||
|     required String? uname, |     required String? uname, | ||||||
|     required int year, |     required int year, | ||||||
|   | |||||||
| @@ -229,7 +229,7 @@ class _ChatRoomActionMenu extends HookConsumerWidget { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class ChatRoomMemberState with _$ChatRoomMemberState { | sealed class ChatRoomMemberState with _$ChatRoomMemberState { | ||||||
|   const factory ChatRoomMemberState({ |   const factory ChatRoomMemberState({ | ||||||
|     required List<SnChatMember> members, |     required List<SnChatMember> members, | ||||||
|     required bool isLoading, |     required bool isLoading, | ||||||
|   | |||||||
| @@ -310,7 +310,7 @@ class _StickerPackActionMenu extends HookConsumerWidget { | |||||||
| } | } | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class StickerWithPackQuery with _$StickerWithPackQuery { | sealed class StickerWithPackQuery with _$StickerWithPackQuery { | ||||||
|   const factory StickerWithPackQuery({ |   const factory StickerWithPackQuery({ | ||||||
|     required String packId, |     required String packId, | ||||||
|     required String id, |     required String id, | ||||||
|   | |||||||
| @@ -64,7 +64,12 @@ class PostDetailScreen extends HookConsumerWidget { | |||||||
|                 child: Material( |                 child: Material( | ||||||
|                   elevation: 2, |                   elevation: 2, | ||||||
|                   color: Colors.transparent, |                   color: Colors.transparent, | ||||||
|                   child: PostQuickReply(parent: post).padding( |                   child: PostQuickReply( | ||||||
|  |                     parent: post, | ||||||
|  |                     onPosted: () { | ||||||
|  |                       ref.invalidate(postRepliesNotifierProvider(id)); | ||||||
|  |                     }, | ||||||
|  |                   ).padding( | ||||||
|                     bottom: MediaQuery.of(context).padding.bottom + 16, |                     bottom: MediaQuery.of(context).padding.bottom + 16, | ||||||
|                     top: 16, |                     top: 16, | ||||||
|                     horizontal: 16, |                     horizontal: 16, | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ const List<Tour> kAllTours = [ | |||||||
| ]; | ]; | ||||||
|  |  | ||||||
| @freezed | @freezed | ||||||
| abstract class Tour with _$Tour { | sealed class Tour with _$Tour { | ||||||
|   const Tour._(); |   const Tour._(); | ||||||
|  |  | ||||||
|   const factory Tour({required String id, required bool isStartup}) = _Tour; |   const factory Tour({required String id, required bool isStartup}) = _Tour; | ||||||
|   | |||||||
| @@ -1,13 +1,59 @@ | |||||||
| import 'package:dio/dio.dart'; |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||||
| import 'package:island/models/post.dart'; | import 'package:island/models/post.dart'; | ||||||
| import 'package:island/pods/network.dart'; | import 'package:island/pods/network.dart'; | ||||||
| import 'package:island/services/responsive.dart'; | import 'package:island/services/responsive.dart'; | ||||||
|  | import 'package:island/widgets/content/paging_helper_ext.dart'; | ||||||
| import 'package:island/widgets/post/post_item.dart'; | import 'package:island/widgets/post/post_item.dart'; | ||||||
| import 'package:island/widgets/response.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'; | ||||||
| import 'package:very_good_infinite_list/very_good_infinite_list.dart'; |  | ||||||
|  | part 'post_replies.g.dart'; | ||||||
|  |  | ||||||
|  | @riverpod | ||||||
|  | class PostRepliesNotifier extends _$PostRepliesNotifier | ||||||
|  |     with CursorPagingNotifierMixin<SnPost> { | ||||||
|  |   static const int _pageSize = 20; | ||||||
|  |  | ||||||
|  |   PostRepliesNotifier(); | ||||||
|  |  | ||||||
|  |   String? _postId; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<CursorPagingData<SnPost>> build(String postId) { | ||||||
|  |     _postId = postId; | ||||||
|  |     return fetch(cursor: null); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Future<CursorPagingData<SnPost>> fetch({required String? cursor}) async { | ||||||
|  |     if (_postId == null) { | ||||||
|  |       throw StateError('PostRepliesNotifier must be initialized with postId'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     final client = ref.read(apiClientProvider); | ||||||
|  |     final offset = cursor == null ? 0 : int.parse(cursor); | ||||||
|  |  | ||||||
|  |     final response = await client.get( | ||||||
|  |       '/posts/$_postId/replies', | ||||||
|  |       queryParameters: {'offset': offset, 'take': _pageSize}, | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     final total = int.parse(response.headers.value('X-Total') ?? '0'); | ||||||
|  |     final List<dynamic> data = response.data; | ||||||
|  |     final posts = data.map((json) => SnPost.fromJson(json)).toList(); | ||||||
|  |  | ||||||
|  |     final hasMore = offset + posts.length < total; | ||||||
|  |     final nextCursor = hasMore ? (offset + posts.length).toString() : null; | ||||||
|  |  | ||||||
|  |     return CursorPagingData( | ||||||
|  |       items: posts, | ||||||
|  |       hasMore: hasMore, | ||||||
|  |       nextCursor: nextCursor, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| class PostRepliesList extends HookConsumerWidget { | class PostRepliesList extends HookConsumerWidget { | ||||||
|   final String postId; |   final String postId; | ||||||
| @@ -15,96 +61,46 @@ class PostRepliesList extends HookConsumerWidget { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context, WidgetRef ref) { |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|     final postAsync = ref.watch(postRepliesProvider(postId)); |  | ||||||
|     final isWide = isWideScreen(context); |     final isWide = isWideScreen(context); | ||||||
|  |  | ||||||
|     return postAsync.when( |     return PagingHelperSliverView( | ||||||
|       data: |       provider: postRepliesNotifierProvider(postId), | ||||||
|           (controller) => SliverInfiniteList( |       futureRefreshable: postRepliesNotifierProvider(postId).future, | ||||||
|             itemCount: controller.posts.length, |       notifierRefreshable: postRepliesNotifierProvider(postId).notifier, | ||||||
|             isLoading: controller.isLoading, |       contentBuilder: (data, widgetCount, endItemView) { | ||||||
|             hasReachedMax: controller.hasReachedMax, |         if (data.items.isEmpty) { | ||||||
|             onFetchData: controller.fetchMore, |           return SliverToBoxAdapter( | ||||||
|             itemBuilder: (context, index) { |             child: Column( | ||||||
|               final post = controller.posts[index]; |               children: [ | ||||||
|               return PostItem( |                 Text( | ||||||
|                 item: post, |                   'No replies', | ||||||
|                 backgroundColor: isWide ? Colors.transparent : null, |                   textAlign: TextAlign.center, | ||||||
|               ); |                 ).fontSize(18).bold(), | ||||||
|             }, |                 const Text('Why not start a discussion?'), | ||||||
|             separatorBuilder: (_, __) => const Divider(height: 1), |               ], | ||||||
|             emptyBuilder: (context) { |             ).padding(vertical: 16), | ||||||
|               return SliverToBoxAdapter( |           ); | ||||||
|                 child: Column( |         } | ||||||
|                   children: [ |  | ||||||
|                     Text( |         return SliverList.builder( | ||||||
|                       'No replies', |           itemCount: widgetCount, | ||||||
|                       textAlign: TextAlign.center, |           itemBuilder: (context, index) { | ||||||
|                     ).fontSize(18).bold(), |             if (index == widgetCount - 1) { | ||||||
|                     Text('Why not start a discussion?'), |               return endItemView; | ||||||
|                   ], |             } | ||||||
|                 ).padding(vertical: 16), |  | ||||||
|               ); |             return Column( | ||||||
|             }, |               children: [ | ||||||
|           ), |                 PostItem( | ||||||
|       loading: |                   item: data.items[index], | ||||||
|           () => SliverFillRemaining( |                   backgroundColor: isWide ? Colors.transparent : null, | ||||||
|             child: const Center(child: CircularProgressIndicator()), |                 ), | ||||||
|           ), |                 const Divider(height: 1), | ||||||
|       error: |               ], | ||||||
|           (e, _) => SliverFillRemaining( |             ); | ||||||
|             child: ResponseErrorWidget( |           }, | ||||||
|               error: e, |         ); | ||||||
|               onRetry: () { |       }, | ||||||
|                 ref.invalidate(postRepliesProvider(postId)); |  | ||||||
|               }, |  | ||||||
|             ), |  | ||||||
|           ), |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| final postRepliesProvider = |  | ||||||
|     FutureProviderFamily<_PostRepliesController, String>((ref, postId) async { |  | ||||||
|       final client = ref.watch(apiClientProvider); |  | ||||||
|       final controller = _PostRepliesController(client, postId); |  | ||||||
|       await controller.fetchMore(); |  | ||||||
|       return controller; |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
| class _PostRepliesController { |  | ||||||
|   _PostRepliesController(this._dio, this.parentId); |  | ||||||
|  |  | ||||||
|   final Dio _dio; |  | ||||||
|   final String parentId; |  | ||||||
|   final List<SnPost> posts = []; |  | ||||||
|   bool isLoading = false; |  | ||||||
|   bool hasReachedMax = false; |  | ||||||
|   int offset = 0; |  | ||||||
|   final int take = 20; |  | ||||||
|   int total = 0; |  | ||||||
|  |  | ||||||
|   Future<void> fetchMore() async { |  | ||||||
|     if (isLoading || hasReachedMax) return; |  | ||||||
|     isLoading = true; |  | ||||||
|  |  | ||||||
|     final response = await _dio.get( |  | ||||||
|       '/posts/$parentId/replies', |  | ||||||
|       queryParameters: {'offset': offset, 'take': take}, |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     final List<SnPost> fetched = |  | ||||||
|         (response.data as List) |  | ||||||
|             .map((e) => SnPost.fromJson(e as Map<String, dynamic>)) |  | ||||||
|             .toList(); |  | ||||||
|  |  | ||||||
|     final headerTotal = int.tryParse(response.headers['x-total']?.first ?? ''); |  | ||||||
|     if (headerTotal != null) total = headerTotal; |  | ||||||
|  |  | ||||||
|     posts.addAll(fetched); |  | ||||||
|     offset += fetched.length; |  | ||||||
|     if (posts.length >= total) hasReachedMax = true; |  | ||||||
|  |  | ||||||
|     isLoading = false; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										180
									
								
								lib/widgets/post/post_replies.g.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								lib/widgets/post/post_replies.g.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | |||||||
|  | // GENERATED CODE - DO NOT MODIFY BY HAND | ||||||
|  |  | ||||||
|  | part of 'post_replies.dart'; | ||||||
|  |  | ||||||
|  | // ************************************************************************** | ||||||
|  | // RiverpodGenerator | ||||||
|  | // ************************************************************************** | ||||||
|  |  | ||||||
|  | String _$postRepliesNotifierHash() => | ||||||
|  |     r'49c178102ec0a4136974a0e9a8f090f511abd542'; | ||||||
|  |  | ||||||
|  | /// Copied from Dart SDK | ||||||
|  | class _SystemHash { | ||||||
|  |   _SystemHash._(); | ||||||
|  |  | ||||||
|  |   static int combine(int hash, int value) { | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + value); | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | ||||||
|  |     return hash ^ (hash >> 6); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static int finish(int hash) { | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | ||||||
|  |     // ignore: parameter_assignments | ||||||
|  |     hash = hash ^ (hash >> 11); | ||||||
|  |     return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | abstract class _$PostRepliesNotifier | ||||||
|  |     extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnPost>> { | ||||||
|  |   late final String postId; | ||||||
|  |  | ||||||
|  |   FutureOr<CursorPagingData<SnPost>> build(String postId); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// See also [PostRepliesNotifier]. | ||||||
|  | @ProviderFor(PostRepliesNotifier) | ||||||
|  | const postRepliesNotifierProvider = PostRepliesNotifierFamily(); | ||||||
|  |  | ||||||
|  | /// See also [PostRepliesNotifier]. | ||||||
|  | class PostRepliesNotifierFamily | ||||||
|  |     extends Family<AsyncValue<CursorPagingData<SnPost>>> { | ||||||
|  |   /// See also [PostRepliesNotifier]. | ||||||
|  |   const PostRepliesNotifierFamily(); | ||||||
|  |  | ||||||
|  |   /// See also [PostRepliesNotifier]. | ||||||
|  |   PostRepliesNotifierProvider call(String postId) { | ||||||
|  |     return PostRepliesNotifierProvider(postId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   PostRepliesNotifierProvider getProviderOverride( | ||||||
|  |     covariant PostRepliesNotifierProvider provider, | ||||||
|  |   ) { | ||||||
|  |     return call(provider.postId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static const Iterable<ProviderOrFamily>? _dependencies = null; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Iterable<ProviderOrFamily>? get dependencies => _dependencies; | ||||||
|  |  | ||||||
|  |   static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Iterable<ProviderOrFamily>? get allTransitiveDependencies => | ||||||
|  |       _allTransitiveDependencies; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String? get name => r'postRepliesNotifierProvider'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// See also [PostRepliesNotifier]. | ||||||
|  | class PostRepliesNotifierProvider | ||||||
|  |     extends | ||||||
|  |         AutoDisposeAsyncNotifierProviderImpl< | ||||||
|  |           PostRepliesNotifier, | ||||||
|  |           CursorPagingData<SnPost> | ||||||
|  |         > { | ||||||
|  |   /// See also [PostRepliesNotifier]. | ||||||
|  |   PostRepliesNotifierProvider(String postId) | ||||||
|  |     : this._internal( | ||||||
|  |         () => PostRepliesNotifier()..postId = postId, | ||||||
|  |         from: postRepliesNotifierProvider, | ||||||
|  |         name: r'postRepliesNotifierProvider', | ||||||
|  |         debugGetCreateSourceHash: | ||||||
|  |             const bool.fromEnvironment('dart.vm.product') | ||||||
|  |                 ? null | ||||||
|  |                 : _$postRepliesNotifierHash, | ||||||
|  |         dependencies: PostRepliesNotifierFamily._dependencies, | ||||||
|  |         allTransitiveDependencies: | ||||||
|  |             PostRepliesNotifierFamily._allTransitiveDependencies, | ||||||
|  |         postId: postId, | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |   PostRepliesNotifierProvider._internal( | ||||||
|  |     super._createNotifier, { | ||||||
|  |     required super.name, | ||||||
|  |     required super.dependencies, | ||||||
|  |     required super.allTransitiveDependencies, | ||||||
|  |     required super.debugGetCreateSourceHash, | ||||||
|  |     required super.from, | ||||||
|  |     required this.postId, | ||||||
|  |   }) : super.internal(); | ||||||
|  |  | ||||||
|  |   final String postId; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   FutureOr<CursorPagingData<SnPost>> runNotifierBuild( | ||||||
|  |     covariant PostRepliesNotifier notifier, | ||||||
|  |   ) { | ||||||
|  |     return notifier.build(postId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Override overrideWith(PostRepliesNotifier Function() create) { | ||||||
|  |     return ProviderOverride( | ||||||
|  |       origin: this, | ||||||
|  |       override: PostRepliesNotifierProvider._internal( | ||||||
|  |         () => create()..postId = postId, | ||||||
|  |         from: from, | ||||||
|  |         name: null, | ||||||
|  |         dependencies: null, | ||||||
|  |         allTransitiveDependencies: null, | ||||||
|  |         debugGetCreateSourceHash: null, | ||||||
|  |         postId: postId, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   AutoDisposeAsyncNotifierProviderElement< | ||||||
|  |     PostRepliesNotifier, | ||||||
|  |     CursorPagingData<SnPost> | ||||||
|  |   > | ||||||
|  |   createElement() { | ||||||
|  |     return _PostRepliesNotifierProviderElement(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) { | ||||||
|  |     return other is PostRepliesNotifierProvider && other.postId == postId; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   int get hashCode { | ||||||
|  |     var hash = _SystemHash.combine(0, runtimeType.hashCode); | ||||||
|  |     hash = _SystemHash.combine(hash, postId.hashCode); | ||||||
|  |  | ||||||
|  |     return _SystemHash.finish(hash); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @Deprecated('Will be removed in 3.0. Use Ref instead') | ||||||
|  | // ignore: unused_element | ||||||
|  | mixin PostRepliesNotifierRef | ||||||
|  |     on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnPost>> { | ||||||
|  |   /// The parameter `postId` of this provider. | ||||||
|  |   String get postId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class _PostRepliesNotifierProviderElement | ||||||
|  |     extends | ||||||
|  |         AutoDisposeAsyncNotifierProviderElement< | ||||||
|  |           PostRepliesNotifier, | ||||||
|  |           CursorPagingData<SnPost> | ||||||
|  |         > | ||||||
|  |     with PostRepliesNotifierRef { | ||||||
|  |   _PostRepliesNotifierProviderElement(super.provider); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   String get postId => (origin as PostRepliesNotifierProvider).postId; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 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 | ||||||
		Reference in New Issue
	
	Block a user