💄 Redesign the post tags and categories page
This commit is contained in:
@@ -102,7 +102,7 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
// Listen for post creation events to refresh activities
|
||||
useEffect(() {
|
||||
final subscription = eventBus.on<PostCreatedEvent>().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: [
|
||||
|
||||
@@ -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<SnPostCategory>
|
||||
>(PostCategoriesNotifier.new);
|
||||
|
||||
class PostCategoriesNotifier extends AsyncNotifier<List<SnPostCategory>>
|
||||
with AsyncPaginationController<SnPostCategory> {
|
||||
@override
|
||||
Future<List<SnPostCategory>> 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, List<SnPostTag>>(
|
||||
PostTagsNotifier.new,
|
||||
);
|
||||
|
||||
class PostTagsNotifier extends AsyncNotifier<List<SnPostTag>>
|
||||
with AsyncPaginationController<SnPostTag> {
|
||||
@override
|
||||
Future<List<SnPostTag>> 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},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user