diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 8a69fb38..a08e2879 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -618,6 +618,7 @@ "tagsHint": "Enter tags, separated by commas", "categories": "Categories", "categoriesHint": "Enter categories, separated by commas", + "categoriesAndTags": "Categories & Tags", "chatNotJoined": "You have not joined this chat yet.", "chatUnableJoin": "You can't join this chat due to it's access control settings.", "chatJoin": "Join the Chat", diff --git a/lib/pods/post/post_categories.dart b/lib/pods/post/post_categories.dart new file mode 100644 index 00000000..fa2f8110 --- /dev/null +++ b/lib/pods/post/post_categories.dart @@ -0,0 +1,52 @@ +// Post Categories Notifier +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/post_category.dart'; +import 'package:island/models/post_tag.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/pods/paging.dart'; + +final postCategoriesProvider = + AsyncNotifierProvider.autoDispose< + PostCategoriesNotifier, + List + >(PostCategoriesNotifier.new); + +class PostCategoriesNotifier extends AsyncNotifier> + with AsyncPaginationController { + @override + Future> fetch() async { + final client = ref.read(apiClientProvider); + + final response = await client.get( + '/sphere/posts/categories', + queryParameters: {'offset': fetchedCount, 'take': 20, 'order': 'usage'}, + ); + + totalCount = int.parse(response.headers.value('X-Total') ?? '0'); + final data = response.data as List; + return data.map((json) => SnPostCategory.fromJson(json)).toList(); + } +} + +// Post Tags Notifier +final postTagsProvider = + AsyncNotifierProvider.autoDispose>( + PostTagsNotifier.new, + ); + +class PostTagsNotifier extends AsyncNotifier> + with AsyncPaginationController { + @override + Future> fetch() async { + final client = ref.read(apiClientProvider); + + final response = await client.get( + '/sphere/posts/tags', + queryParameters: {'offset': fetchedCount, 'take': 20, 'order': 'usage'}, + ); + + totalCount = int.parse(response.headers.value('X-Total') ?? '0'); + final data = response.data as List; + return data.map((json) => SnPostTag.fromJson(json)).toList(); + } +} diff --git a/lib/route.dart b/lib/route.dart index 8f24dfae..9e9b7cbf 100644 --- a/lib/route.dart +++ b/lib/route.dart @@ -105,10 +105,9 @@ final routerProvider = Provider((ref) { GoRoute( name: 'articleCompose', path: '/articles/compose', - builder: - (context, state) => ArticleComposeScreen( - initialState: state.extra as PostComposeInitialState?, - ), + builder: (context, state) => ArticleComposeScreen( + initialState: state.extra as PostComposeInitialState?, + ), ), GoRoute( name: 'articleEdit', @@ -190,12 +189,11 @@ final routerProvider = Provider((ref) { GoRoute( name: 'explore', path: '/', - pageBuilder: - (context, state) => CustomTransitionPage( - key: const ValueKey('explore'), - child: const ExploreScreen(), - transitionsBuilder: _tabPagesTransitionBuilder, - ), + pageBuilder: (context, state) => CustomTransitionPage( + key: const ValueKey('explore'), + child: const ExploreScreen(), + transitionsBuilder: _tabPagesTransitionBuilder, + ), ), GoRoute( name: 'postSearch', @@ -220,11 +218,6 @@ final routerProvider = Provider((ref) { return PostCategoryDetailScreen(slug: slug, isCategory: true); }, ), - GoRoute( - name: 'postTags', - path: '/posts/tags', - builder: (context, state) => const PostTagsListScreen(), - ), GoRoute( name: 'postTagDetail', path: '/posts/tags/:slug', @@ -260,12 +253,11 @@ final routerProvider = Provider((ref) { // Chat tab ShellRoute( - pageBuilder: - (context, state, child) => CustomTransitionPage( - key: const ValueKey('chat'), - child: ChatShellScreen(child: child), - transitionsBuilder: _tabPagesTransitionBuilder, - ), + pageBuilder: (context, state, child) => CustomTransitionPage( + key: const ValueKey('chat'), + child: ChatShellScreen(child: child), + transitionsBuilder: _tabPagesTransitionBuilder, + ), routes: [ GoRoute( name: 'chatList', @@ -303,12 +295,11 @@ final routerProvider = Provider((ref) { GoRoute( name: 'realmList', path: '/realms', - pageBuilder: - (context, state) => CustomTransitionPage( - key: const ValueKey('realms'), - child: const RealmListScreen(), - transitionsBuilder: _tabPagesTransitionBuilder, - ), + pageBuilder: (context, state) => CustomTransitionPage( + key: const ValueKey('realms'), + child: const RealmListScreen(), + transitionsBuilder: _tabPagesTransitionBuilder, + ), routes: [ GoRoute( name: 'realmNew', @@ -336,12 +327,11 @@ final routerProvider = Provider((ref) { // Account tab ShellRoute( - pageBuilder: - (context, state, child) => CustomTransitionPage( - key: const ValueKey('account'), - child: AccountShellScreen(child: child), - transitionsBuilder: _tabPagesTransitionBuilder, - ), + pageBuilder: (context, state, child) => CustomTransitionPage( + key: const ValueKey('account'), + child: AccountShellScreen(child: child), + transitionsBuilder: _tabPagesTransitionBuilder, + ), routes: [ GoRoute( name: 'account', @@ -352,8 +342,8 @@ final routerProvider = Provider((ref) { GoRoute( name: 'stickerMarketplace', path: '/stickers', - builder: - (context, state) => const MarketplaceStickersScreen(), + builder: (context, state) => + const MarketplaceStickersScreen(), routes: [ GoRoute( name: 'stickerPackDetail', @@ -368,8 +358,8 @@ final routerProvider = Provider((ref) { GoRoute( name: 'webFeedMarketplace', path: '/feeds', - builder: - (context, state) => const MarketplaceWebFeedsScreen(), + builder: (context, state) => + const MarketplaceWebFeedsScreen(), routes: [ GoRoute( name: 'webFeedDetail', @@ -516,29 +506,25 @@ final routerProvider = Provider((ref) { GoRoute( name: 'developerHub', path: '/developers', - builder: - (context, state) => DeveloperHubScreen( - initialPublisherName: - state.uri.queryParameters['publisher'], - initialProjectId: state.uri.queryParameters['project'], - ), + builder: (context, state) => DeveloperHubScreen( + initialPublisherName: state.uri.queryParameters['publisher'], + initialProjectId: state.uri.queryParameters['project'], + ), routes: [ GoRoute( name: 'developerProjectNew', path: ':name/projects/new', - builder: - (context, state) => NewProjectScreen( - publisherName: state.pathParameters['name']!, - ), + builder: (context, state) => NewProjectScreen( + publisherName: state.pathParameters['name']!, + ), ), GoRoute( name: 'developerProjectEdit', path: ':name/projects/:id/edit', - builder: - (context, state) => EditProjectScreen( - publisherName: state.pathParameters['name']!, - id: state.pathParameters['id']!, - ), + builder: (context, state) => EditProjectScreen( + publisherName: state.pathParameters['name']!, + id: state.pathParameters['id']!, + ), ), GoRoute( name: 'developerProjectDetail', @@ -558,22 +544,20 @@ final routerProvider = Provider((ref) { GoRoute( name: 'developerAppDetail', path: 'apps/:appId', - builder: - (context, state) => AppDetailScreen( - publisherName: state.pathParameters['name']!, - projectId: state.pathParameters['projectId']!, - appId: state.pathParameters['appId']!, - ), + builder: (context, state) => AppDetailScreen( + publisherName: state.pathParameters['name']!, + projectId: state.pathParameters['projectId']!, + appId: state.pathParameters['appId']!, + ), ), GoRoute( name: 'developerBotDetail', path: 'bots/:botId', - builder: - (context, state) => BotDetailScreen( - publisherName: state.pathParameters['name']!, - projectId: state.pathParameters['projectId']!, - botId: state.pathParameters['botId']!, - ), + builder: (context, state) => BotDetailScreen( + publisherName: state.pathParameters['name']!, + projectId: state.pathParameters['projectId']!, + botId: state.pathParameters['botId']!, + ), ), ], ), diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index eea56569..be119a5e 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -102,7 +102,7 @@ class ExploreScreen extends HookConsumerWidget { // Listen for post creation events to refresh activities useEffect(() { final subscription = eventBus.on().listen((event) { - ref.invalidate(activityListProvider); + ref.read(activityListProvider.notifier).refresh(); }); return subscription.cancel; }, []); @@ -183,25 +183,13 @@ class ExploreScreen extends HookConsumerWidget { children: [ const Icon(Symbols.category), const Gap(12), - Text('categories').tr(), + Text('categoriesAndTags').tr(), ], ), onTap: () { context.pushNamed('postCategories'); }, ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.label), - const Gap(12), - Text('tags').tr(), - ], - ), - onTap: () { - context.pushNamed('postTags'); - }, - ), PopupMenuItem( child: Row( children: [ @@ -490,25 +478,13 @@ class ExploreScreen extends HookConsumerWidget { children: [ const Icon(Symbols.category), const Gap(12), - Text('categories').tr(), + Text('categoriesAndTags').tr(), ], ), onTap: () { context.pushNamed('postCategories'); }, ), - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.label), - const Gap(12), - Text('tags').tr(), - ], - ), - onTap: () { - context.pushNamed('postTags'); - }, - ), PopupMenuItem( child: Row( children: [ diff --git a/lib/screens/posts/post_categories_list.dart b/lib/screens/posts/post_categories_list.dart index c446168c..a0bf640e 100644 --- a/lib/screens/posts/post_categories_list.dart +++ b/lib/screens/posts/post_categories_list.dart @@ -2,118 +2,113 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:island/models/post_category.dart'; -import 'package:island/models/post_tag.dart'; -import 'package:island/pods/network.dart'; -import 'package:island/pods/paging.dart'; +import 'package:island/pods/post/post_categories.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/paging/pagination_list.dart'; import 'package:material_symbols_icons/symbols.dart'; -// Post Categories Notifier -final postCategoriesProvider = AsyncNotifierProvider.autoDispose< - PostCategoriesNotifier, - List ->(PostCategoriesNotifier.new); - -class PostCategoriesNotifier extends AsyncNotifier> - with AsyncPaginationController { - @override - Future> fetch() async { - final client = ref.read(apiClientProvider); - - final response = await client.get( - '/sphere/posts/categories', - queryParameters: {'offset': fetchedCount, 'take': 20, 'order': 'usage'}, - ); - - totalCount = int.parse(response.headers.value('X-Total') ?? '0'); - final data = response.data as List; - return data.map((json) => SnPostCategory.fromJson(json)).toList(); - } -} - -// Post Tags Notifier -final postTagsProvider = - AsyncNotifierProvider.autoDispose>( - PostTagsNotifier.new, - ); - -class PostTagsNotifier extends AsyncNotifier> - with AsyncPaginationController { - @override - Future> fetch() async { - final client = ref.read(apiClientProvider); - - final response = await client.get( - '/sphere/posts/tags', - queryParameters: {'offset': fetchedCount, 'take': 20, 'order': 'usage'}, - ); - - totalCount = int.parse(response.headers.value('X-Total') ?? '0'); - final data = response.data as List; - return data.map((json) => SnPostTag.fromJson(json)).toList(); - } -} - -class PostCategoriesListScreen extends ConsumerWidget { +class PostCategoriesListScreen extends HookConsumerWidget { const PostCategoriesListScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - return AppScaffold( - appBar: AppBar(title: const Text('categories').tr()), - body: PaginationList( - provider: postCategoriesProvider, - notifier: postCategoriesProvider.notifier, - padding: EdgeInsets.zero, - itemBuilder: (context, index, category) { - return ListTile( - leading: const Icon(Symbols.category), - contentPadding: const EdgeInsets.symmetric(horizontal: 24), - trailing: const Icon(Symbols.chevron_right), - title: Text(category.categoryDisplayTitle), - subtitle: Text('postCount'.plural(category.usage)), - onTap: () { - context.pushNamed( - 'postCategoryDetail', - pathParameters: {'slug': category.slug}, - ); - }, - ); - }, + return DefaultTabController( + length: 2, + child: AppScaffold( + isNoBackground: false, + appBar: AppBar( + title: const Text('categoriesAndTags').tr(), + bottom: TabBar( + tabs: [ + Tab( + child: Text( + 'categories'.tr(), + style: TextStyle( + color: Theme.of(context).appBarTheme.foregroundColor, + ), + ), + ), + Tab( + child: Text( + 'tags'.tr(), + style: TextStyle( + color: Theme.of(context).appBarTheme.foregroundColor, + ), + ), + ), + ], + ), + ), + body: const TabBarView(children: [_CategoriesTab(), _TagsTab()]), ), ); } } -class PostTagsListScreen extends ConsumerWidget { - const PostTagsListScreen({super.key}); +class _CategoriesTab extends ConsumerWidget { + const _CategoriesTab(); @override Widget build(BuildContext context, WidgetRef ref) { - return AppScaffold( - appBar: AppBar(title: const Text('tags').tr()), - body: PaginationList( - provider: postTagsProvider, - notifier: postTagsProvider.notifier, - padding: EdgeInsets.zero, - itemBuilder: (context, index, tag) { - return ListTile( - title: Text(tag.name ?? '#${tag.slug}'), - contentPadding: const EdgeInsets.symmetric(horizontal: 24), - leading: const Icon(Symbols.label), - trailing: const Icon(Symbols.chevron_right), - subtitle: Text('postCount'.plural(tag.usage)), - onTap: () { - context.pushNamed( - 'postTagDetail', - pathParameters: {'slug': tag.slug}, - ); - }, - ); - }, - ), + return PaginationList( + provider: postCategoriesProvider, + notifier: postCategoriesProvider.notifier, + footerSkeletonMaxWidth: 640, + padding: EdgeInsets.zero, + itemBuilder: (context, index, category) { + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 640), + child: ListTile( + leading: const Icon(Symbols.category), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + trailing: const Icon(Symbols.chevron_right), + title: Text(category.categoryDisplayTitle), + subtitle: Text('postCount'.plural(category.usage)), + onTap: () { + context.pushNamed( + 'postCategoryDetail', + pathParameters: {'slug': category.slug}, + ); + }, + ), + ), + ); + }, + ); + } +} + +class _TagsTab extends ConsumerWidget { + const _TagsTab(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return PaginationList( + provider: postTagsProvider, + notifier: postTagsProvider.notifier, + footerSkeletonMaxWidth: 640, + padding: EdgeInsets.zero, + itemBuilder: (context, index, tag) { + return Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 640), + child: ListTile( + title: Text(tag.name ?? '#${tag.slug}'), + contentPadding: const EdgeInsets.symmetric(horizontal: 24), + leading: const Icon(Symbols.label), + trailing: const Icon(Symbols.chevron_right), + subtitle: Text('postCount'.plural(tag.usage)), + onTap: () { + context.pushNamed( + 'postTagDetail', + pathParameters: {'slug': tag.slug}, + ); + }, + ), + ), + ); + }, ); } } diff --git a/lib/widgets/paging/pagination_list.dart b/lib/widgets/paging/pagination_list.dart index 19f0f3c7..8c7d2db8 100644 --- a/lib/widgets/paging/pagination_list.dart +++ b/lib/widgets/paging/pagination_list.dart @@ -22,6 +22,7 @@ class PaginationList extends HookConsumerWidget { final bool showDefaultWidgets; final EdgeInsets? padding; final Widget? footerSkeletonChild; + final double? footerSkeletonMaxWidth; const PaginationList({ super.key, required this.provider, @@ -32,6 +33,7 @@ class PaginationList extends HookConsumerWidget { this.showDefaultWidgets = true, this.padding, this.footerSkeletonChild, + this.footerSkeletonMaxWidth, }); @override @@ -53,7 +55,9 @@ class PaginationList extends HookConsumerWidget { ).colorScheme.surfaceContainerHighest, ), containersColor: Theme.of(context).colorScheme.surfaceContainerLow, - child: footerSkeletonChild ?? const _DefaultSkeletonChild(), + child: + footerSkeletonChild ?? + _DefaultSkeletonChild(maxWidth: footerSkeletonMaxWidth), ), ); return SliverList.list(children: content); @@ -75,6 +79,7 @@ class PaginationList extends HookConsumerWidget { noti: noti, data: data, skeletonChild: footerSkeletonChild, + skeletonMaxWidth: footerSkeletonMaxWidth, ); } final entry = data.value?[idx]; @@ -102,7 +107,9 @@ class PaginationList extends HookConsumerWidget { ).colorScheme.surfaceContainerHighest, ), containersColor: Theme.of(context).colorScheme.surfaceContainerLow, - child: footerSkeletonChild ?? const _DefaultSkeletonChild(), + child: + footerSkeletonChild ?? + _DefaultSkeletonChild(maxWidth: footerSkeletonMaxWidth), ), ); return SizedBox( @@ -128,6 +135,7 @@ class PaginationList extends HookConsumerWidget { noti: noti, data: data, skeletonChild: footerSkeletonChild, + skeletonMaxWidth: footerSkeletonMaxWidth, ); } final entry = data.value?[idx]; @@ -159,6 +167,7 @@ class PaginationWidget extends HookConsumerWidget { final bool isSliver; final bool showDefaultWidgets; final Widget? footerSkeletonChild; + final double? footerSkeletonMaxWidth; const PaginationWidget({ super.key, required this.provider, @@ -168,6 +177,7 @@ class PaginationWidget extends HookConsumerWidget { this.isSliver = false, this.showDefaultWidgets = true, this.footerSkeletonChild, + this.footerSkeletonMaxWidth, }); @override @@ -189,7 +199,9 @@ class PaginationWidget extends HookConsumerWidget { ).colorScheme.surfaceContainerHighest, ), containersColor: Theme.of(context).colorScheme.surfaceContainerLow, - child: footerSkeletonChild ?? const _DefaultSkeletonChild(), + child: + footerSkeletonChild ?? + _DefaultSkeletonChild(maxWidth: footerSkeletonMaxWidth), ), ); return SliverList.list(children: content); @@ -207,6 +219,7 @@ class PaginationWidget extends HookConsumerWidget { noti: noti, data: data, skeletonChild: footerSkeletonChild, + skeletonMaxWidth: footerSkeletonMaxWidth, ); final content = contentBuilder(data.value ?? [], footer); @@ -229,7 +242,9 @@ class PaginationWidget extends HookConsumerWidget { ).colorScheme.surfaceContainerHighest, ), containersColor: Theme.of(context).colorScheme.surfaceContainerLow, - child: footerSkeletonChild ?? const _DefaultSkeletonChild(), + child: + footerSkeletonChild ?? + _DefaultSkeletonChild(maxWidth: footerSkeletonMaxWidth), ), ); return SizedBox( @@ -250,6 +265,7 @@ class PaginationWidget extends HookConsumerWidget { noti: noti, data: data, skeletonChild: footerSkeletonChild, + skeletonMaxWidth: footerSkeletonMaxWidth, ); final content = contentBuilder(data.value ?? [], footer); @@ -272,6 +288,7 @@ class PaginationListFooter extends HookConsumerWidget { final PaginationController noti; final AsyncValue> data; final Widget? skeletonChild; + final double? skeletonMaxWidth; final bool isSliver; const PaginationListFooter({ @@ -279,6 +296,7 @@ class PaginationListFooter extends HookConsumerWidget { required this.noti, required this.data, this.skeletonChild, + this.skeletonMaxWidth, this.isSliver = false, }); @@ -293,7 +311,7 @@ class PaginationListFooter extends HookConsumerWidget { highlightColor: Theme.of(context).colorScheme.surfaceContainerHighest, ), containersColor: Theme.of(context).colorScheme.surfaceContainerLow, - child: skeletonChild ?? _DefaultSkeletonChild(), + child: skeletonChild ?? _DefaultSkeletonChild(maxWidth: skeletonMaxWidth), ); final child = hasBeenVisible.value ? data.isLoading @@ -322,14 +340,24 @@ class PaginationListFooter extends HookConsumerWidget { } class _DefaultSkeletonChild extends StatelessWidget { - const _DefaultSkeletonChild(); + final double? maxWidth; + const _DefaultSkeletonChild({this.maxWidth}); @override Widget build(BuildContext context) { - return ListTile( + final content = ListTile( title: Text('Some data'), subtitle: const Text('Subtitle here'), trailing: const Icon(Icons.ac_unit), ); + if (maxWidth != null) { + return Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: maxWidth!), + child: content, + ), + ); + } + return content; } } diff --git a/lib/widgets/post/compose_settings_sheet.dart b/lib/widgets/post/compose_settings_sheet.dart index ade069c3..782b99be 100644 --- a/lib/widgets/post/compose_settings_sheet.dart +++ b/lib/widgets/post/compose_settings_sheet.dart @@ -9,33 +9,14 @@ import 'package:island/models/post_category.dart'; import 'package:island/models/post_tag.dart'; import 'package:island/models/realm.dart'; import 'package:island/pods/network.dart'; +import 'package:island/pods/post/post_categories.dart'; import 'package:island/screens/realm/realms.dart'; import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/post/compose_shared.dart'; import 'package:material_symbols_icons/symbols.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:styled_widget/styled_widget.dart'; -part 'compose_settings_sheet.g.dart'; - -@riverpod -Future> postCategories(Ref ref) async { - final apiClient = ref.watch(apiClientProvider); - final resp = await apiClient.get('/sphere/posts/categories'); - final categories = - resp.data - .map((e) => SnPostCategory.fromJson(e)) - .cast() - .toList(); - // Remove duplicates based on id - final uniqueCategories = {}; - for (final category in categories) { - uniqueCategories[category.id] = category; - } - return uniqueCategories.values.toList(); -} - class ComposeSettingsSheet extends HookConsumerWidget { final ComposeState state; @@ -121,39 +102,38 @@ class ComposeSettingsSheet extends HookConsumerWidget { void showVisibilitySheet() { showModalBottomSheet( context: context, - builder: - (context) => SheetScaffold( - titleText: 'postVisibility'.tr(), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - buildVisibilityOption( - context, - 0, - Symbols.public, - 'postVisibilityPublic', - ), - buildVisibilityOption( - context, - 1, - Symbols.group, - 'postVisibilityFriends', - ), - buildVisibilityOption( - context, - 2, - Symbols.link_off, - 'postVisibilityUnlisted', - ), - buildVisibilityOption( - context, - 3, - Symbols.lock, - 'postVisibilityPrivate', - ), - ], + builder: (context) => SheetScaffold( + titleText: 'postVisibility'.tr(), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + buildVisibilityOption( + context, + 0, + Symbols.public, + 'postVisibilityPublic', ), - ), + buildVisibilityOption( + context, + 1, + Symbols.group, + 'postVisibilityFriends', + ), + buildVisibilityOption( + context, + 2, + Symbols.link_off, + 'postVisibilityUnlisted', + ), + buildVisibilityOption( + context, + 3, + Symbols.lock, + 'postVisibilityPrivate', + ), + ], + ), + ), ); } @@ -182,8 +162,8 @@ class ComposeSettingsSheet extends HookConsumerWidget { borderRadius: BorderRadius.circular(12), ), ), - onTapOutside: - (_) => FocusManager.instance.primaryFocus?.unfocus(), + onTapOutside: (_) => + FocusManager.instance.primaryFocus?.unfocus(), ), // Tags field @@ -209,51 +189,48 @@ class ComposeSettingsSheet extends HookConsumerWidget { Wrap( spacing: 8, runSpacing: 8, - children: - currentTags.map((tag) { - return Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: BorderRadius.circular(16), + children: currentTags.map((tag) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: BorderRadius.circular(16), + ), + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + '#$tag', + style: TextStyle( + color: Theme.of( + context, + ).colorScheme.onPrimary, + fontSize: 14, + ), ), - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, + const Gap(4), + InkWell( + onTap: () { + final newTags = List.from( + state.tags.value, + )..remove(tag); + state.tags.value = newTags; + }, + child: Icon( + Icons.close, + size: 16, + color: Theme.of( + context, + ).colorScheme.onPrimary, + ), ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - '#$tag', - style: TextStyle( - color: - Theme.of( - context, - ).colorScheme.onPrimary, - fontSize: 14, - ), - ), - const Gap(4), - InkWell( - onTap: () { - final newTags = List.from( - state.tags.value, - )..remove(tag); - state.tags.value = newTags; - }, - child: Icon( - Icons.close, - size: 16, - color: - Theme.of( - context, - ).colorScheme.onPrimary, - ), - ), - ], - ), - ); - }).toList(), + ], + ), + ); + }).toList(), ), // Tag input with autocomplete TypeAheadField( @@ -274,8 +251,8 @@ class ComposeSettingsSheet extends HookConsumerWidget { }, ); }, - suggestionsCallback: - (pattern) => _fetchTagSuggestions(pattern, ref), + suggestionsCallback: (pattern) => + _fetchTagSuggestions(pattern, ref), itemBuilder: (context, suggestion) { return ListTile( shape: const RoundedRectangleBorder( @@ -314,55 +291,49 @@ class ComposeSettingsSheet extends HookConsumerWidget { ), ), hint: Text('categories'.tr(), style: TextStyle(fontSize: 15)), - items: - (postCategories.value ?? []).map((item) { - return DropdownMenuItem( - value: item, - enabled: false, - child: StatefulBuilder( - builder: (context, menuSetState) { - final isSelected = state.categories.value.contains( - item, - ); - return InkWell( - onTap: () { - isSelected - ? state.categories.value = - state.categories.value - .where((e) => e != item) - .toList() - : state.categories.value = [ - ...state.categories.value, - item, - ]; - menuSetState(() {}); - }, - child: Container( - height: double.infinity, - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Row( - children: [ - if (isSelected) - const Icon(Icons.check_box_outlined) - else - const Icon(Icons.check_box_outline_blank), - const SizedBox(width: 16), - Expanded( - child: Text( - item.categoryDisplayTitle, - style: const TextStyle(fontSize: 14), - ), - ), - ], - ), - ), - ); + items: (postCategories.value ?? []).map((item) { + return DropdownMenuItem( + value: item, + enabled: false, + child: StatefulBuilder( + builder: (context, menuSetState) { + final isSelected = state.categories.value.contains(item); + return InkWell( + onTap: () { + isSelected + ? state.categories.value = state.categories.value + .where((e) => e != item) + .toList() + : state.categories.value = [ + ...state.categories.value, + item, + ]; + menuSetState(() {}); }, - ), - ); - }).toList(), + child: Container( + height: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Row( + children: [ + if (isSelected) + const Icon(Icons.check_box_outlined) + else + const Icon(Icons.check_box_outline_blank), + const SizedBox(width: 16), + Expanded( + child: Text( + item.categoryDisplayTitle, + style: const TextStyle(fontSize: 14), + ), + ), + ], + ), + ), + ); + }, + ), + ); + }).toList(), value: currentCategories.isEmpty ? null : currentCategories.last, onChanged: (_) {}, selectedItemBuilder: (context) { diff --git a/lib/widgets/post/compose_settings_sheet.g.dart b/lib/widgets/post/compose_settings_sheet.g.dart deleted file mode 100644 index 7b57a3ef..00000000 --- a/lib/widgets/post/compose_settings_sheet.g.dart +++ /dev/null @@ -1,51 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'compose_settings_sheet.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint, type=warning - -@ProviderFor(postCategories) -const postCategoriesProvider = PostCategoriesProvider._(); - -final class PostCategoriesProvider - extends - $FunctionalProvider< - AsyncValue>, - List, - FutureOr> - > - with - $FutureModifier>, - $FutureProvider> { - const PostCategoriesProvider._() - : super( - from: null, - argument: null, - retry: null, - name: r'postCategoriesProvider', - isAutoDispose: true, - dependencies: null, - $allTransitiveDependencies: null, - ); - - @override - String debugGetCreateSourceHash() => _$postCategoriesHash(); - - @$internal - @override - $FutureProviderElement> $createElement( - $ProviderPointer pointer, - ) => $FutureProviderElement(pointer); - - @override - FutureOr> create(Ref ref) { - return postCategories(ref); - } -} - -String _$postCategoriesHash() => r'8799c10eb91cf8c8c7ea72eff3475e1eaa7b9a2b';