diff --git a/lib/pods/webfeed.dart b/lib/pods/webfeed.dart index d0e440b7..72b72361 100644 --- a/lib/pods/webfeed.dart +++ b/lib/pods/webfeed.dart @@ -5,16 +5,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:island/models/webfeed.dart'; import 'package:island/pods/network.dart'; -final webFeedListProvider = FutureProvider.family, String>(( - ref, - pubName, -) async { - final client = ref.watch(apiClientProvider); - final response = await client.get('/sphere/publishers/$pubName/feeds'); - return (response.data as List) - .map((json) => SnWebFeed.fromJson(json)) - .toList(); -}); +final webFeedListProvider = FutureProvider.autoDispose + .family, String>((ref, pubName) async { + final client = ref.watch(apiClientProvider); + final response = await client.get('/sphere/publishers/$pubName/feeds'); + return (response.data as List) + .map((json) => SnWebFeed.fromJson(json)) + .toList(); + }); class WebFeedNotifier extends AsyncNotifier { final ({String pubName, String? feedId}) arg; @@ -51,10 +49,9 @@ class WebFeedNotifier extends AsyncNotifier { final client = ref.read(apiClientProvider); final url = '/sphere/publishers/${feed.publisherId}/feeds'; - final response = - feed.id.isEmpty - ? await client.post(url, data: feed.toJson()) - : await client.patch('$url/${feed.id}', data: feed.toJson()); + final response = feed.id.isEmpty + ? await client.post(url, data: feed.toJson()) + : await client.patch('$url/${feed.id}', data: feed.toJson()); state = AsyncValue.data(SnWebFeed.fromJson(response.data)); } catch (error, stackTrace) { diff --git a/lib/screens/chat/chat.dart b/lib/screens/chat/chat.dart index e3dca118..75d605fe 100644 --- a/lib/screens/chat/chat.dart +++ b/lib/screens/chat/chat.dart @@ -20,6 +20,7 @@ import 'package:island/widgets/navigation/fab_menu.dart'; import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:relative_time/relative_time.dart'; +import 'package:skeletonizer/skeletonizer.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; import 'package:island/pods/chat/chat_room.dart'; @@ -50,84 +51,124 @@ class ChatRoomListTile extends HookConsumerWidget { if (validMembers.isNotEmpty) { final userInfo = ref.watch(userInfoProvider); if (userInfo.value != null) { - validMembers = - validMembers - .where((e) => e.accountId != userInfo.value!.id) - .toList(); + validMembers = validMembers + .where((e) => e.accountId != userInfo.value!.id) + .toList(); } } Widget buildSubtitle() { if (subtitle != null) return subtitle!; - return summary.when( - data: (data) { - if (data == null) { - return isDirect && room.description == null - ? Text( - validMembers.map((e) => '@${e.account.name}').join(', '), - maxLines: 1, - ) - : Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - if (data.unreadCount > 0) - Text( - 'unreadMessages'.plural(data.unreadCount), - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.primary, + return AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + layoutBuilder: (currentChild, previousChildren) => Stack( + alignment: Alignment.centerLeft, + children: [ + ...previousChildren, + if (currentChild != null) currentChild, + ], + ), + child: summary.when( + data: (data) => Container( + key: const ValueKey('data'), + child: data == null + ? isDirect && room.description == null + ? Text( + validMembers + .map((e) => '@${e.account.name}') + .join(', '), + maxLines: 1, + ) + : Text( + room.description ?? 'descriptionNone'.tr(), + maxLines: 1, + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (data.unreadCount > 0) + Text( + 'unreadMessages'.plural(data.unreadCount), + style: Theme.of(context).textTheme.bodySmall + ?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + if (data.lastMessage == null) + Text( + room.description ?? 'descriptionNone'.tr(), + maxLines: 1, + ) + else + Row( + spacing: 4, + children: [ + Badge( + label: Text( + data.lastMessage!.sender.account.nick, + ), + textColor: Theme.of( + context, + ).colorScheme.onPrimary, + backgroundColor: Theme.of( + context, + ).colorScheme.primary, + ), + Expanded( + child: Text( + (data.lastMessage!.content?.isNotEmpty ?? false) + ? data.lastMessage!.content! + : 'messageNone'.tr(), + 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, + ), + ), + ], + ), + ], ), - ), - if (data.lastMessage == null) - Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1) - else - Row( - spacing: 4, - children: [ - Badge( - label: Text(data.lastMessage!.sender.account.nick), - textColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: Theme.of(context).colorScheme.primary, - ), - Expanded( - child: Text( - (data.lastMessage!.content?.isNotEmpty ?? false) - ? data.lastMessage!.content! - : 'messageNone'.tr(), - 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, - ), - ), - ], - ), - ], - ); - }, - loading: () => const SizedBox.shrink(), - error: - (_, _) => - isDirect && room.description == null - ? Text( - validMembers.map((e) => '@${e.account.name}').join(', '), - maxLines: 1, - ) - : Text( - room.description ?? 'descriptionNone'.tr(), - maxLines: 1, - ), + ), + loading: () => Container( + key: const ValueKey('loading'), + child: Builder( + builder: (context) { + final seed = DateTime.now().microsecondsSinceEpoch; + final len = 4 + (seed % 17); // 4..20 inclusive + const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + var s = seed; + final buffer = StringBuffer(); + for (var i = 0; i < len; i++) { + s = (s * 1103515245 + 12345) & 0x7fffffff; + buffer.write(chars[s % chars.length]); + } + return Skeletonizer( + enabled: true, + child: Text(buffer.toString()), + ); + }, + ), + ), + error: (_, _) => Container( + key: const ValueKey('error'), + child: isDirect && room.description == null + ? Text( + validMembers.map((e) => '@${e.account.name}').join(', '), + maxLines: 1, + ) + : Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1), + ), + ), ); } @@ -149,17 +190,15 @@ class ChatRoomListTile extends HookConsumerWidget { loading: () => false, error: (_, _) => false, ), - child: - (isDirect && room.picture?.id == null) - ? SplitAvatarWidget( - filesId: - validMembers - .map((e) => e.account.profile.picture?.id) - .toList(), - ) - : room.picture?.id == null - ? CircleAvatar(child: Text(room.name![0].toUpperCase())) - : ProfilePictureWidget(fileId: room.picture?.id), + child: (isDirect && room.picture?.id == null) + ? SplitAvatarWidget( + filesId: validMembers + .map((e) => e.account.profile.picture?.id) + .toList(), + ) + : room.picture?.id == null + ? CircleAvatar(child: Text(room.name![0].toUpperCase())) + : ProfilePictureWidget(fileId: room.picture?.id), ), title: Text(titleText), subtitle: buildSubtitle(), @@ -199,74 +238,67 @@ class ChatListBodyWidget extends HookConsumerWidget { builder: (context, ref, _) { final summaryState = ref.watch(chatSummaryProvider); return summaryState.maybeWhen( - loading: - () => const LinearProgressIndicator( - minHeight: 2, - borderRadius: BorderRadius.zero, - ), + loading: () => const LinearProgressIndicator( + minHeight: 2, + borderRadius: BorderRadius.zero, + ), orElse: () => const SizedBox.shrink(), ); }, ), Expanded( child: chats.when( - data: - (items) => RefreshIndicator( - onRefresh: - () => Future.sync(() { - ref.invalidate(chatRoomJoinedProvider); - }), - child: SuperListView.builder( - padding: EdgeInsets.only(bottom: 96), - itemCount: - items - .where( - (item) => - selectedTab.value == 0 || - (selectedTab.value == 1 && item.type == 1) || - (selectedTab.value == 2 && item.type != 1), - ) - .length, - itemBuilder: (context, index) { - final filteredItems = - items - .where( - (item) => - selectedTab.value == 0 || - (selectedTab.value == 1 && - item.type == 1) || - (selectedTab.value == 2 && item.type != 1), - ) - .toList(); - final item = filteredItems[index]; - return ChatRoomListTile( - room: item, - isDirect: item.type == 1, - onTap: () { - if (isWideScreen(context)) { - context.replaceNamed( - 'chatRoom', - pathParameters: {'id': item.id}, - ); - } else { - context.pushNamed( - 'chatRoom', - pathParameters: {'id': item.id}, - ); - } - }, - ); + data: (items) => RefreshIndicator( + onRefresh: () => Future.sync(() { + ref.invalidate(chatRoomJoinedProvider); + }), + child: SuperListView.builder( + padding: EdgeInsets.only(bottom: 96), + itemCount: items + .where( + (item) => + selectedTab.value == 0 || + (selectedTab.value == 1 && item.type == 1) || + (selectedTab.value == 2 && item.type != 1), + ) + .length, + itemBuilder: (context, index) { + final filteredItems = items + .where( + (item) => + selectedTab.value == 0 || + (selectedTab.value == 1 && item.type == 1) || + (selectedTab.value == 2 && item.type != 1), + ) + .toList(); + final item = filteredItems[index]; + return ChatRoomListTile( + room: item, + isDirect: item.type == 1, + onTap: () { + if (isWideScreen(context)) { + context.replaceNamed( + 'chatRoom', + pathParameters: {'id': item.id}, + ); + } else { + context.pushNamed( + 'chatRoom', + pathParameters: {'id': item.id}, + ); + } }, - ), - ), + ); + }, + ), + ), loading: () => const Center(child: CircularProgressIndicator()), - error: - (error, stack) => ResponseErrorWidget( - error: error, - onRetry: () { - ref.invalidate(chatRoomJoinedProvider); - }, - ), + error: (error, stack) => ResponseErrorWidget( + error: error, + onRetry: () { + ref.invalidate(chatRoomJoinedProvider); + }, + ), ), ), ], @@ -552,53 +584,47 @@ class _ChatInvitesSheet extends HookConsumerWidget { ), ], child: invites.when( - data: - (items) => - items.isEmpty - ? Center( - child: - Text( - 'invitesEmpty', - textAlign: TextAlign.center, - ).tr(), - ) - : ListView.builder( - shrinkWrap: true, - itemCount: items.length, - itemBuilder: (context, index) { - final invite = items[index]; - return ChatRoomListTile( - room: invite.chatRoom!, - isDirect: invite.chatRoom!.type == 1, - subtitle: Row( - spacing: 6, - children: [ - if (invite.chatRoom!.type == 1) - Badge( - label: const Text('directMessage').tr(), - backgroundColor: - Theme.of(context).colorScheme.primary, - textColor: - Theme.of(context).colorScheme.onPrimary, - ), - ], + data: (items) => items.isEmpty + ? Center( + child: Text('invitesEmpty', textAlign: TextAlign.center).tr(), + ) + : ListView.builder( + shrinkWrap: true, + itemCount: items.length, + itemBuilder: (context, index) { + final invite = items[index]; + return ChatRoomListTile( + room: invite.chatRoom!, + isDirect: invite.chatRoom!.type == 1, + subtitle: Row( + spacing: 6, + children: [ + if (invite.chatRoom!.type == 1) + Badge( + label: const Text('directMessage').tr(), + backgroundColor: Theme.of( + context, + ).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Symbols.check), - onPressed: () => acceptInvite(invite), - ), - IconButton( - icon: const Icon(Symbols.close), - onPressed: () => declineInvite(invite), - ), - ], - ), - ); - }, + ], ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Symbols.check), + onPressed: () => acceptInvite(invite), + ), + IconButton( + icon: const Icon(Symbols.close), + onPressed: () => declineInvite(invite), + ), + ], + ), + ); + }, + ), loading: () => const Center(child: CircularProgressIndicator()), error: (error, stack) => Center(child: Text('Error: $error')), ), diff --git a/lib/screens/creators/poll/poll_list.dart b/lib/screens/creators/poll/poll_list.dart index 23f0f306..18f066a6 100644 --- a/lib/screens/creators/poll/poll_list.dart +++ b/lib/screens/creators/poll/poll_list.dart @@ -44,11 +44,10 @@ class PollListNotifier extends AsyncNotifier> queryParameters: queryParams, ); totalCount = int.parse(response.headers.value('X-Total') ?? '0'); - final items = - response.data - .map((json) => SnPollWithStats.fromJson(json)) - .cast() - .toList(); + final items = response.data + .map((json) => SnPollWithStats.fromJson(json)) + .cast() + .toList(); return items; } @@ -91,6 +90,7 @@ class CreatorPollListScreen extends HookConsumerWidget { body: ExtendedRefreshIndicator( onRefresh: () => ref.refresh(pollListNotifierProvider(pubName).future), child: PaginationList( + footerSkeletonMaxWidth: 640, provider: pollListNotifierProvider(pubName), notifier: pollListNotifierProvider(pubName).notifier, padding: const EdgeInsets.only(top: 12), @@ -119,10 +119,9 @@ class _CreatorPollItem extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final theme = Theme.of(context); final ended = pollWithStats.endedAt; - final endedText = - ended == null - ? 'No end' - : MaterialLocalizations.of(context).formatFullDate(ended); + final endedText = ended == null + ? 'No end' + : MaterialLocalizations.of(context).formatFullDate(ended); return Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), @@ -152,78 +151,69 @@ class _CreatorPollItem extends HookConsumerWidget { ], ), trailing: PopupMenuButton( - itemBuilder: - (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.edit), - const Gap(16), - Text('edit').tr(), + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.edit), + const Gap(16), + Text('edit').tr(), + ], + ), + onTap: () async { + final result = await showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + builder: (context) => PollEditorScreen( + initialPublisher: pubName, + initialPollId: pollWithStats.id, + ), + ); + if (result != null && context.mounted) { + ref.invalidate(pollListNotifierProvider(pubName)); + } + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.delete, color: Colors.red), + const Gap(16), + Text('delete').tr().textColor(Colors.red), + ], + ), + onTap: () async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Delete Poll'), + content: Text('Are you sure you want to delete this poll?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text('Delete'), + ), ], ), - onTap: () async { - final result = await showModalBottomSheet( - context: context, - isScrollControlled: true, - isDismissible: false, - builder: - (context) => PollEditorScreen( - initialPublisher: pubName, - initialPollId: pollWithStats.id, - ), - ); - if (result != null && context.mounted) { - ref.invalidate(pollListNotifierProvider(pubName)); - } - }, - ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.delete, color: Colors.red), - const Gap(16), - Text('delete').tr().textColor(Colors.red), - ], - ), - onTap: () async { - final confirmed = await showDialog( - context: context, - builder: - (context) => AlertDialog( - title: Text('Delete Poll'), - content: Text( - 'Are you sure you want to delete this poll?', - ), - actions: [ - TextButton( - onPressed: - () => Navigator.of(context).pop(false), - child: Text('Cancel'), - ), - TextButton( - onPressed: - () => Navigator.of(context).pop(true), - child: Text('Delete'), - ), - ], - ), - ); - if (confirmed == true) { - try { - final client = ref.read(apiClientProvider); - await client.delete( - '/sphere/polls/${pollWithStats.id}', - ); - ref.invalidate(pollListNotifierProvider(pubName)); - showSnackBar('Poll deleted successfully'); - } catch (e) { - showErrorAlert(e); - } - } - }, - ), - ], + ); + if (confirmed == true) { + try { + final client = ref.read(apiClientProvider); + await client.delete('/sphere/polls/${pollWithStats.id}'); + ref.invalidate(pollListNotifierProvider(pubName)); + showSnackBar('Poll deleted successfully'); + } catch (e) { + showErrorAlert(e); + } + } + }, + ), + ], ), onTap: () { showModalBottomSheet( diff --git a/lib/screens/creators/sites/site_list.dart b/lib/screens/creators/sites/site_list.dart index ec090a0a..f468fd6a 100644 --- a/lib/screens/creators/sites/site_list.dart +++ b/lib/screens/creators/sites/site_list.dart @@ -12,7 +12,6 @@ import 'package:island/widgets/alert.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/paging/pagination_list.dart'; import 'package:material_symbols_icons/symbols.dart'; -import 'package:island/widgets/extended_refresh_indicator.dart'; import 'package:styled_widget/styled_widget.dart'; final siteListNotifierProvider = AsyncNotifierProvider.family.autoDispose( @@ -38,11 +37,10 @@ class SiteListNotifier extends AsyncNotifier> queryParameters: queryParams, ); totalCount = int.parse(response.headers.value('X-Total') ?? '0'); - final items = - response.data - .map((json) => SnPublicationSite.fromJson(json)) - .cast() - .toList(); + final items = response.data + .map((json) => SnPublicationSite.fromJson(json)) + .cast() + .toList(); return items; } @@ -70,23 +68,17 @@ class CreatorSiteListScreen extends HookConsumerWidget { onPressed: () => _createSite(context), child: Icon(Icons.add), ), - body: ExtendedRefreshIndicator( - onRefresh: () => ref.refresh(siteListNotifierProvider(pubName).future), - child: CustomScrollView( - slivers: [ - const SliverGap(8), - PaginationList( - provider: siteListNotifierProvider(pubName), - notifier: siteListNotifierProvider(pubName).notifier, - itemBuilder: (context, index, site) { - return ConstrainedBox( - constraints: BoxConstraints(maxWidth: 640), - child: _CreatorSiteItem(site: site, pubName: pubName), - ).center(); - }, - ), - ], - ), + body: PaginationList( + footerSkeletonMaxWidth: 640, + provider: siteListNotifierProvider(pubName), + notifier: siteListNotifierProvider(pubName).notifier, + padding: const EdgeInsets.only(top: 12), + itemBuilder: (context, index, site) { + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: 640), + child: _CreatorSiteItem(site: site, pubName: pubName), + ).center(); + }, ), ); } @@ -148,73 +140,65 @@ class _CreatorSiteItem extends HookConsumerWidget { ), ), PopupMenuButton( - itemBuilder: - (context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.edit), - const Gap(16), - Text('edit').tr(), + itemBuilder: (context) => [ + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.edit), + const Gap(16), + Text('edit').tr(), + ], + ), + onTap: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => + SiteForm(pubName: pubName, siteSlug: site.slug), + ); + }, + ), + PopupMenuItem( + child: Row( + children: [ + const Icon(Symbols.delete, color: Colors.red), + const Gap(16), + Text('delete').tr().textColor(Colors.red), + ], + ), + onTap: () async { + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('deleteSite'.tr()), + content: Text('deleteSiteConfirm'.tr()), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: Text('cancel'.tr()), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text('delete'.tr()), + ), ], ), - onTap: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: - (context) => SiteForm( - pubName: pubName, - siteSlug: site.slug, - ), + ); + if (confirmed == true) { + try { + final client = ref.read(apiClientProvider); + await client.delete( + '/zone/sites/$pubName/${site.slug}', ); - }, - ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.delete, color: Colors.red), - const Gap(16), - Text('delete').tr().textColor(Colors.red), - ], - ), - onTap: () async { - final confirmed = await showDialog( - context: context, - builder: - (context) => AlertDialog( - title: Text('deleteSite'.tr()), - content: Text('deleteSiteConfirm'.tr()), - actions: [ - TextButton( - onPressed: - () => - Navigator.of(context).pop(false), - child: Text('cancel'.tr()), - ), - TextButton( - onPressed: - () => Navigator.of(context).pop(true), - child: Text('delete'.tr()), - ), - ], - ), - ); - if (confirmed == true) { - try { - final client = ref.read(apiClientProvider); - await client.delete( - '/zone/sites/$pubName/${site.slug}', - ); - ref.invalidate(siteListNotifierProvider(pubName)); - showSnackBar('siteDeletedSuccess'.tr()); - } catch (e) { - showErrorAlert(e); - } - } - }, - ), - ], + ref.invalidate(siteListNotifierProvider(pubName)); + showSnackBar('siteDeletedSuccess'.tr()); + } catch (e) { + showErrorAlert(e); + } + } + }, + ), + ], ), ], ), diff --git a/lib/screens/creators/stickers/stickers.dart b/lib/screens/creators/stickers/stickers.dart index 08441b93..6dcef2a1 100644 --- a/lib/screens/creators/stickers/stickers.dart +++ b/lib/screens/creators/stickers/stickers.dart @@ -41,11 +41,10 @@ class StickersScreen extends HookConsumerWidget { showModalBottomSheet( context: context, isScrollControlled: true, - builder: - (context) => SheetScaffold( - titleText: 'createStickerPack'.tr(), - child: StickerPackForm(pubName: pubName), - ), + builder: (context) => SheetScaffold( + titleText: 'createStickerPack'.tr(), + child: StickerPackForm(pubName: pubName), + ), ).then((value) { if (value != null) { ref.invalidate(stickerPacksProvider(pubName)); @@ -54,24 +53,23 @@ class StickersScreen extends HookConsumerWidget { }, child: const Icon(Symbols.add), ), - body: - isWideScreen(context) - ? Center( - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: 640), - child: Card( - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(8), - topRight: Radius.circular(8), - ), + body: isWideScreen(context) + ? Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 640), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(8), + topRight: Radius.circular(8), ), - margin: const EdgeInsets.only(top: 16), - child: content, ), + margin: const EdgeInsets.only(top: 16), + child: content, ), - ) - : content, + ), + ) + : content, ); } } @@ -83,6 +81,7 @@ class SliverStickerPacksList extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return PaginationList( + padding: EdgeInsets.zero, provider: stickerPacksProvider(pubName), notifier: stickerPacksProvider(pubName).notifier, itemBuilder: (context, index, sticker) { @@ -97,40 +96,38 @@ class SliverStickerPacksList extends HookConsumerWidget { showModalBottomSheet( context: context, isScrollControlled: true, - builder: - (context) => SheetScaffold( - titleText: sticker.name, - actions: [ - IconButton( - icon: const Icon(Symbols.add_circle), - onPressed: () { - final id = sticker.id; - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: - (context) => SheetScaffold( - titleText: 'createSticker'.tr(), - child: StickerForm(packId: id), - ), - ).then((value) { - if (value != null) { - ref.invalidate(stickerPackContentProvider(id)); - } - }); - }, - ), - StickerPackActionMenu( - pubName: pubName, - packId: sticker.id, - iconShadow: Shadow(), - ), - ], - child: StickerPackDetailContent( - id: sticker.id, - pubName: pubName, - ), + builder: (context) => SheetScaffold( + titleText: sticker.name, + actions: [ + IconButton( + icon: const Icon(Symbols.add_circle), + onPressed: () { + final id = sticker.id; + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => SheetScaffold( + titleText: 'createSticker'.tr(), + child: StickerForm(packId: id), + ), + ).then((value) { + if (value != null) { + ref.invalidate(stickerPackContentProvider(id)); + } + }); + }, ), + StickerPackActionMenu( + pubName: pubName, + packId: sticker.id, + iconShadow: Shadow(), + ), + ], + child: StickerPackDetailContent( + id: sticker.id, + pubName: pubName, + ), + ), ); }, ); @@ -165,11 +162,10 @@ class StickerPacksNotifier extends AsyncNotifier> ); totalCount = int.parse(response.headers.value('X-Total') ?? '0'); - final stickers = - response.data - .map((e) => SnStickerPack.fromJson(e)) - .cast() - .toList(); + final stickers = response.data + .map((e) => SnStickerPack.fromJson(e)) + .cast() + .toList(); return stickers; } catch (err) { @@ -262,10 +258,9 @@ class StickerPackForm extends HookConsumerWidget { color: Theme.of(context).colorScheme.surfaceContainer, borderRadius: BorderRadius.all(Radius.circular(8)), ), - child: - (icon.value?.isEmpty ?? true) - ? const SizedBox.shrink() - : CloudImageWidget(fileId: icon.value!), + child: (icon.value?.isEmpty ?? true) + ? const SizedBox.shrink() + : CloudImageWidget(fileId: icon.value!), ), ), ), @@ -273,10 +268,9 @@ class StickerPackForm extends HookConsumerWidget { onPressed: () { showModalBottomSheet( context: context, - builder: - (context) => CloudFilePicker( - allowedTypes: {UniversalFileType.image}, - ), + builder: (context) => CloudFilePicker( + allowedTypes: {UniversalFileType.image}, + ), ).then((value) { if (value == null) return; icon.value = value[0].id; @@ -300,8 +294,8 @@ class StickerPackForm extends HookConsumerWidget { } return null; }, - onTapOutside: - (_) => FocusManager.instance.primaryFocus?.unfocus(), + onTapOutside: (_) => + FocusManager.instance.primaryFocus?.unfocus(), ), TextFormField( controller: descriptionController, @@ -314,8 +308,8 @@ class StickerPackForm extends HookConsumerWidget { ), minLines: 3, maxLines: null, - onTapOutside: - (_) => FocusManager.instance.primaryFocus?.unfocus(), + onTapOutside: (_) => + FocusManager.instance.primaryFocus?.unfocus(), ), TextFormField( controller: prefixController, @@ -332,8 +326,8 @@ class StickerPackForm extends HookConsumerWidget { } return null; }, - onTapOutside: - (_) => FocusManager.instance.primaryFocus?.unfocus(), + onTapOutside: (_) => + FocusManager.instance.primaryFocus?.unfocus(), ), ], ),