Show post tags and categories

This commit is contained in:
2025-08-08 21:18:25 +08:00
parent 4b253ac3ec
commit b25e8d661a
15 changed files with 191 additions and 155 deletions

View File

@@ -6,7 +6,6 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post_category.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/text.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/post/compose_shared.dart';
import 'package:material_symbols_icons/symbols.dart';
@@ -16,12 +15,12 @@ import 'package:textfield_tags/textfield_tags.dart';
part 'compose_settings_sheet.g.dart';
@riverpod
Future<List<PostCategory>> postCategories(Ref ref) async {
Future<List<SnPostCategory>> postCategories(Ref ref) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/sphere/posts/categories');
return resp.data
.map((e) => PostCategory.fromJson(e))
.cast<PostCategory>()
.map((e) => SnPostCategory.fromJson(e))
.cast<SnPostCategory>()
.toList();
}
@@ -215,14 +214,6 @@ class ComposeSettingsSheet extends HookConsumerWidget {
);
}
String getCategoryDisplayTitle(PostCategory category) {
final capitalizedSlug = category.slug.capitalizeEachWord();
if ('postCategory$capitalizedSlug'.trExists()) {
return 'postCategory$capitalizedSlug'.tr();
}
return category.name ?? category.slug;
}
return SheetScaffold(
titleText: 'postSettings'.tr(),
child: SingleChildScrollView(
@@ -253,7 +244,7 @@ class ComposeSettingsSheet extends HookConsumerWidget {
// Categories field
// FIXME: Sometimes the entire dropdown crashes: 'package:flutter/src/rendering/stack.dart': Failed assertion: line 799 pos 12: 'firstChild == null || child != null': is not true.
DropdownButtonFormField2<PostCategory>(
DropdownButtonFormField2<SnPostCategory>(
isExpanded: true,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(vertical: 9),
@@ -263,7 +254,7 @@ class ComposeSettingsSheet extends HookConsumerWidget {
),
hint: Text('categories'.tr(), style: TextStyle(fontSize: 15)),
items:
(postCategories.value ?? <PostCategory>[]).map((item) {
(postCategories.value ?? <SnPostCategory>[]).map((item) {
return DropdownMenuItem(
value: item,
enabled: false,
@@ -299,7 +290,7 @@ class ComposeSettingsSheet extends HookConsumerWidget {
const SizedBox(width: 16),
Expanded(
child: Text(
getCategoryDisplayTitle(item),
item.categoryDisplayTitle,
style: const TextStyle(fontSize: 14),
),
),
@@ -331,7 +322,7 @@ class ComposeSettingsSheet extends HookConsumerWidget {
),
margin: const EdgeInsets.only(right: 4),
child: Text(
getCategoryDisplayTitle(category),
category.categoryDisplayTitle,
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
fontSize: 13,

View File

@@ -6,12 +6,12 @@ part of 'compose_settings_sheet.dart';
// RiverpodGenerator
// **************************************************************************
String _$postCategoriesHash() => r'503ce6f0fdd728a8cb991665006c3b4bffbb94d4';
String _$postCategoriesHash() => r'24337fe806d088b6468a350f62d5a5d40232a73c';
/// See also [postCategories].
@ProviderFor(postCategories)
final postCategoriesProvider =
AutoDisposeFutureProvider<List<PostCategory>>.internal(
AutoDisposeFutureProvider<List<SnPostCategory>>.internal(
postCategories,
name: r'postCategoriesProvider',
debugGetCreateSourceHash:
@@ -24,6 +24,6 @@ final postCategoriesProvider =
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef PostCategoriesRef = AutoDisposeFutureProviderRef<List<PostCategory>>;
typedef PostCategoriesRef = AutoDisposeFutureProviderRef<List<SnPostCategory>>;
// 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

View File

@@ -31,7 +31,7 @@ class ComposeState {
final ValueNotifier<Map<int, double>> attachmentProgress;
final ValueNotifier<SnPublisher?> currentPublisher;
final ValueNotifier<bool> submitting;
final ValueNotifier<List<PostCategory>> categories;
final ValueNotifier<List<SnPostCategory>> categories;
StringTagController tagsController;
final String draftId;
int postType;
@@ -109,7 +109,7 @@ class ComposeLogic {
attachmentProgress: ValueNotifier<Map<int, double>>({}),
currentPublisher: ValueNotifier<SnPublisher?>(originalPost?.publisher),
tagsController: tagsController,
categories: ValueNotifier<List<PostCategory>>(
categories: ValueNotifier<List<SnPostCategory>>(
originalPost?.categories ?? [],
),
draftId: id,
@@ -140,7 +140,7 @@ class ComposeLogic {
attachmentProgress: ValueNotifier<Map<int, double>>({}),
currentPublisher: ValueNotifier<SnPublisher?>(null),
tagsController: tagsController,
categories: ValueNotifier<List<PostCategory>>([]),
categories: ValueNotifier<List<SnPostCategory>>([]),
draftId: draft.id,
postType: postType,
pollId: null,

View File

@@ -315,7 +315,7 @@ class PostItem extends HookConsumerWidget {
}
}
String _parseVisibility(int visibility) {
String parseVisibility(int visibility) {
switch (visibility) {
case 1:
return 'postVisibilityFriends';
@@ -377,12 +377,10 @@ class PostItem extends HookConsumerWidget {
if (item.editedAt != null)
Text(
'editedAt'.tr(args: [item.editedAt!.formatSystem()]),
style: TextStyle(height: 1.2),
).fontSize(10),
if (item.visibility != 0)
Text(
_parseVisibility(item.visibility).tr(),
style: TextStyle(height: 1.45),
parseVisibility(item.visibility).tr(),
).fontSize(10),
],
),
@@ -573,6 +571,35 @@ class PostItem extends HookConsumerWidget {
vertical: 4,
),
),
if (item.tags.isNotEmpty)
Wrap(
spacing: 8,
children: [
for (final tag in item.tags)
InkWell(
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 4,
children: [
const Icon(Symbols.label, size: 16),
Text(tag.name ?? tag.slug),
],
),
onTap: () {},
),
for (final category in item.categories)
InkWell(
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 4,
children: [
const Icon(Symbols.category, size: 16),
Text(category.categoryDisplayTitle),
],
),
),
],
).padding(horizontal: renderingPadding.horizontal + 4, top: 4),
if (item.meta?['embeds'] != null)
...((item.meta!['embeds'] as List<dynamic>)
.map((embedData) => convertMapKeysToSnakeCase(embedData))