🎨 Optimized code of post compose

This commit is contained in:
LittleSheep 2025-06-19 00:13:36 +08:00
parent 52111c4b95
commit ab4f4faafe
2 changed files with 298 additions and 244 deletions

View File

@ -63,10 +63,14 @@ class PostComposeScreen extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final publishers = ref.watch(publishersManagedProvider); // Extract common theme and localization to avoid repeated lookups
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final publishers = ref.watch(publishersManagedProvider);
final currentPublisher = useState<SnPublisher?>(null); final currentPublisher = useState<SnPublisher?>(null);
// Initialize publisher once when data is available
useEffect(() { useEffect(() {
if (publishers.value?.isNotEmpty ?? false) { if (publishers.value?.isNotEmpty ?? false) {
currentPublisher.value = publishers.value!.first; currentPublisher.value = publishers.value!.first;
@ -74,7 +78,7 @@ class PostComposeScreen extends HookConsumerWidget {
return null; return null;
}, [publishers]); }, [publishers]);
// Contains the XFile, ByteData, or SnCloudFile // State management
final attachments = useState<List<UniversalFile>>( final attachments = useState<List<UniversalFile>>(
originalPost?.attachments originalPost?.attachments
.map( .map(
@ -100,12 +104,11 @@ class PostComposeScreen extends HookConsumerWidget {
originalPost?.content ?? originalPost?.content ??
(forwardedPost != null ? '> ${forwardedPost!.content}\n\n' : null), (forwardedPost != null ? '> ${forwardedPost!.content}\n\n' : null),
); );
// Add visibility state with default value from original post or 0 (public)
final visibility = useState<int>(originalPost?.visibility ?? 0); final visibility = useState<int>(originalPost?.visibility ?? 0);
final submitting = useState(false); final submitting = useState(false);
final attachmentProgress = useState<Map<int, double>>({});
// Media handling functions
Future<void> pickPhotoMedia() async { Future<void> pickPhotoMedia() async {
final result = await ref final result = await ref
.watch(imagePickerProvider) .watch(imagePickerProvider)
@ -130,16 +133,30 @@ class PostComposeScreen extends HookConsumerWidget {
]; ];
} }
final attachmentProgress = useState<Map<int, double>>({}); // Helper method to get mimetype from file type
String getMimeTypeFromFileType(UniversalFileType type) {
return switch (type) {
UniversalFileType.image => 'image/unknown',
UniversalFileType.video => 'video/unknown',
UniversalFileType.audio => 'audio/unknown',
UniversalFileType.file => 'application/octet-stream',
};
}
// Attachment management functions
Future<void> uploadAttachment(int index) async { Future<void> uploadAttachment(int index) async {
final attachment = attachments.value[index]; final attachment = attachments.value[index];
if (attachment is SnCloudFile) return; if (attachment.isOnCloud) return;
final baseUrl = ref.watch(serverUrlProvider); final baseUrl = ref.watch(serverUrlProvider);
final token = await getToken(ref.watch(tokenProvider)); final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw ArgumentError('Token is null'); if (token == null) throw ArgumentError('Token is null');
try { try {
// Update progress state
attachmentProgress.value = {...attachmentProgress.value, index: 0}; attachmentProgress.value = {...attachmentProgress.value, index: 0};
// Upload file to cloud
final cloudFile = final cloudFile =
await putMediaToCloud( await putMediaToCloud(
fileData: attachment, fileData: attachment,
@ -148,32 +165,45 @@ class PostComposeScreen extends HookConsumerWidget {
filename: attachment.data.name ?? 'Post media', filename: attachment.data.name ?? 'Post media',
mimetype: mimetype:
attachment.data.mimeType ?? attachment.data.mimeType ??
switch (attachment.type) { getMimeTypeFromFileType(attachment.type),
UniversalFileType.image => 'image/unknown', onProgress: (progress, _) {
UniversalFileType.video => 'video/unknown',
UniversalFileType.audio => 'audio/unknown',
UniversalFileType.file => 'application/octet-stream',
},
onProgress: (progress, estimate) {
attachmentProgress.value = { attachmentProgress.value = {
...attachmentProgress.value, ...attachmentProgress.value,
index: progress, index: progress,
}; };
}, },
).future; ).future;
if (cloudFile == null) { if (cloudFile == null) {
throw ArgumentError('Failed to upload the file...'); throw ArgumentError('Failed to upload the file...');
} }
// Update attachments list with cloud file
final clone = List.of(attachments.value); final clone = List.of(attachments.value);
clone[index] = UniversalFile(data: cloudFile, type: attachment.type); clone[index] = UniversalFile(data: cloudFile, type: attachment.type);
attachments.value = clone; attachments.value = clone;
} catch (err) { } catch (err) {
showErrorAlert(err); showErrorAlert(err);
} finally { } finally {
attachmentProgress.value = attachmentProgress.value..remove(index); // Clean up progress state
attachmentProgress.value = {...attachmentProgress.value}..remove(index);
} }
} }
// Helper method to move attachment in the list
List<UniversalFile> moveAttachment(
List<UniversalFile> attachments,
int idx,
int delta,
) {
if (idx + delta < 0 || idx + delta >= attachments.length) {
return attachments;
}
final clone = List.of(attachments);
clone.insert(idx + delta, clone.removeAt(idx));
return clone;
}
Future<void> deleteAttachment(int index) async { Future<void> deleteAttachment(int index) async {
final attachment = attachments.value[index]; final attachment = attachments.value[index];
if (attachment.isOnCloud) { if (attachment.isOnCloud) {
@ -185,38 +215,52 @@ class PostComposeScreen extends HookConsumerWidget {
attachments.value = clone; attachments.value = clone;
} }
// Form submission
Future<void> performAction() async { Future<void> performAction() async {
if (submitting.value) return;
try { try {
submitting.value = true; submitting.value = true;
// Upload any local attachments first
await Future.wait( await Future.wait(
attachments.value attachments.value
.where((e) => e.isOnDevice) .asMap()
.mapIndexed((idx, e) => uploadAttachment(idx)), .entries
.where((entry) => entry.value.isOnDevice)
.map((entry) => uploadAttachment(entry.key)),
); );
// Prepare API request
final client = ref.watch(apiClientProvider); final client = ref.watch(apiClientProvider);
final isNewPost = originalPost == null;
final endpoint = isNewPost ? '/posts' : '/posts/${originalPost!.id}';
// Create request payload
final payload = {
'title': titleController.text,
'description': descriptionController.text,
'content': contentController.text,
'visibility': visibility.value,
'attachments':
attachments.value
.where((e) => e.isOnCloud)
.map((e) => e.data.id)
.toList(),
if (repliedPost != null) 'replied_post_id': repliedPost!.id,
if (forwardedPost != null) 'forwarded_post_id': forwardedPost!.id,
};
// Send request
await client.request( await client.request(
originalPost == null ? '/posts' : '/posts/${originalPost!.id}', endpoint,
data: { data: payload,
'title': titleController.text,
'description': descriptionController.text,
'content': contentController.text,
'visibility':
visibility.value, // Add visibility field to API request
'attachments':
attachments.value
.where((e) => e.isOnCloud)
.map((e) => e.data.id)
.toList(),
if (repliedPost != null) 'replied_post_id': repliedPost!.id,
if (forwardedPost != null) 'forwarded_post_id': forwardedPost!.id,
},
options: Options( options: Options(
headers: {'X-Pub': currentPublisher.value?.name}, headers: {'X-Pub': currentPublisher.value?.name},
method: originalPost == null ? 'POST' : 'PATCH', method: isNewPost ? 'POST' : 'PATCH',
), ),
); );
if (context.mounted) { if (context.mounted) {
context.maybePop(true); context.maybePop(true);
} }
@ -227,6 +271,7 @@ class PostComposeScreen extends HookConsumerWidget {
} }
} }
// Clipboard handling
Future<void> handlePaste() async { Future<void> handlePaste() async {
final clipboard = await Pasteboard.image; final clipboard = await Pasteboard.image;
if (clipboard == null) return; if (clipboard == null) return;
@ -245,60 +290,30 @@ class PostComposeScreen extends HookConsumerWidget {
final isPaste = event.logicalKey == LogicalKeyboardKey.keyV; final isPaste = event.logicalKey == LogicalKeyboardKey.keyV;
final isModifierPressed = event.isMetaPressed || event.isControlPressed; final isModifierPressed = event.isMetaPressed || event.isControlPressed;
final isSubmit = event.logicalKey == LogicalKeyboardKey.enter;
if (isPaste && isModifierPressed) { if (isPaste && isModifierPressed) {
handlePaste(); handlePaste();
} else if (isSubmit && isModifierPressed && !submitting.value) {
performAction();
} }
} }
void showVisibilityModal() { // Helper method to build visibility option
showDialog( Widget buildVisibilityOption(
context: context, BuildContext context,
builder: int value,
(context) => AlertDialog( IconData icon,
title: Text('postVisibility'.tr()), String textKey,
content: Column( ) {
mainAxisSize: MainAxisSize.min, return ListTile(
children: [ leading: Icon(icon),
ListTile( title: Text(textKey.tr()),
leading: Icon(Symbols.public), onTap: () {
title: Text('postVisibilityPublic'.tr()), visibility.value = value;
onTap: () { Navigator.pop(context);
visibility.value = 0; },
Navigator.pop(context); selected: visibility.value == value,
},
selected: visibility.value == 0,
),
ListTile(
leading: Icon(Symbols.group),
title: Text('postVisibilityFriends'.tr()),
onTap: () {
visibility.value = 1;
Navigator.pop(context);
},
selected: visibility.value == 1,
),
ListTile(
leading: Icon(Symbols.link_off),
title: Text('postVisibilityUnlisted'.tr()),
onTap: () {
visibility.value = 2;
Navigator.pop(context);
},
selected: visibility.value == 2,
),
ListTile(
leading: Icon(Symbols.lock),
title: Text('postVisibilityPrivate'.tr()),
onTap: () {
visibility.value = 3;
Navigator.pop(context);
},
selected: visibility.value == 3,
),
],
),
),
); );
} }
@ -330,6 +345,120 @@ class PostComposeScreen extends HookConsumerWidget {
} }
} }
// Visibility handling
void showVisibilityModal() {
showDialog(
context: context,
builder:
(context) => AlertDialog(
title: Text('postVisibility'.tr()),
content: 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',
),
],
),
),
);
}
// Show keyboard shortcuts dialog
void showKeyboardShortcutsDialog() {
showDialog(
context: context,
builder:
(context) => AlertDialog(
title: Text('keyboard_shortcuts'.tr()),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Ctrl/Cmd + Enter: ${'submit'.tr()}'),
Text('Ctrl/Cmd + V: ${'paste'.tr()}'),
Text('Ctrl/Cmd + I: ${'add_image'.tr()}'),
Text('Ctrl/Cmd + Shift + V: ${'add_video'.tr()}'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('close'.tr()),
),
],
),
);
}
// Helper method to build wide attachment grid
Widget buildWideAttachmentGrid(
BoxConstraints constraints,
List<UniversalFile> attachments,
Map<int, double> progress,
) {
return Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (var idx = 0; idx < attachments.length; idx++)
SizedBox(
width: constraints.maxWidth / 2 - 4,
child: AttachmentPreview(
item: attachments[idx],
progress: progress[idx],
onRequestUpload: () => uploadAttachment(idx),
onDelete: () => deleteAttachment(idx),
onMove: (delta) => moveAttachment(attachments, idx, delta),
),
),
],
);
}
// Helper method to build narrow attachment list
Widget buildNarrowAttachmentList(
List<UniversalFile> attachments,
Map<int, double> progress,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
for (var idx = 0; idx < attachments.length; idx++)
AttachmentPreview(
item: attachments[idx],
progress: progress[idx],
onRequestUpload: () => uploadAttachment(idx),
onDelete: () => deleteAttachment(idx),
onMove: (delta) => moveAttachment(attachments, idx, delta),
),
],
);
}
// Build UI
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
@ -343,31 +472,7 @@ class PostComposeScreen extends HookConsumerWidget {
message: 'keyboard_shortcuts'.tr(), message: 'keyboard_shortcuts'.tr(),
child: IconButton( child: IconButton(
icon: const Icon(Symbols.keyboard), icon: const Icon(Symbols.keyboard),
onPressed: () { onPressed: showKeyboardShortcutsDialog,
showDialog(
context: context,
builder:
(context) => AlertDialog(
title: Text('keyboard_shortcuts'.tr()),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Ctrl/Cmd + Enter: ${'submit'.tr()}'),
Text('Ctrl/Cmd + V: ${'paste'.tr()}'),
Text('Ctrl/Cmd + I: ${'add_image'.tr()}'),
Text('Ctrl/Cmd + Shift + V: ${'add_video'.tr()}'),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('close'.tr()),
),
],
),
);
},
), ),
), ),
IconButton( IconButton(
@ -382,9 +487,9 @@ class PostComposeScreen extends HookConsumerWidget {
strokeWidth: 2.5, strokeWidth: 2.5,
), ),
).center() ).center()
: originalPost != null : Icon(
? const Icon(Symbols.edit) originalPost != null ? Symbols.edit : Symbols.upload,
: const Icon(Symbols.upload), ),
), ),
const Gap(8), const Gap(8),
], ],
@ -392,53 +497,29 @@ class PostComposeScreen extends HookConsumerWidget {
body: Column( body: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Reply/Forward info section
if (repliedPost != null) if (repliedPost != null)
Container( _buildInfoBanner(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), context,
color: Theme.of( Symbols.reply,
context, 'reply',
).colorScheme.surfaceVariant.withOpacity(0.5), repliedPost!.publisher.nick,
child: Row(
children: [
const Icon(Symbols.reply, size: 16),
const Gap(8),
Expanded(
child: Text(
'${'reply'.tr()}: ${repliedPost!.publisher.nick}',
style: Theme.of(context).textTheme.bodySmall,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
), ),
if (forwardedPost != null) if (forwardedPost != null)
Container( _buildInfoBanner(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), context,
color: Theme.of( Symbols.forward,
context, 'forward',
).colorScheme.surfaceVariant.withOpacity(0.5), forwardedPost!.publisher.nick,
child: Row(
children: [
const Icon(Symbols.forward, size: 16),
const Gap(8),
Expanded(
child: Text(
'${'forward'.tr()}: ${forwardedPost!.publisher.nick}',
style: Theme.of(context).textTheme.bodySmall,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
), ),
// Main content area
Expanded( Expanded(
child: Row( child: Row(
spacing: 12, spacing: 12,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Publisher profile picture
GestureDetector( GestureDetector(
child: ProfilePictureWidget( child: ProfilePictureWidget(
fileId: currentPublisher.value?.picture?.id, fileId: currentPublisher.value?.picture?.id,
@ -458,28 +539,29 @@ class PostComposeScreen extends HookConsumerWidget {
}); });
}, },
).padding(top: 16), ).padding(top: 16),
// Post content form
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Visibility selector
Row( Row(
children: [ children: [
OutlinedButton( OutlinedButton(
onPressed: () { onPressed: showVisibilityModal,
showVisibilityModal();
},
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
side: BorderSide( side: BorderSide(
color: Theme.of( color: colorScheme.primary.withOpacity(0.5),
context, ),
).colorScheme.primary.withOpacity(0.5), padding: const EdgeInsets.symmetric(
horizontal: 16,
), ),
padding: EdgeInsets.symmetric(horizontal: 16),
visualDensity: const VisualDensity( visualDensity: const VisualDensity(
vertical: -2, vertical: -2,
horizontal: -4, horizontal: -4,
@ -491,16 +573,14 @@ class PostComposeScreen extends HookConsumerWidget {
Icon( Icon(
getVisibilityIcon(visibility.value), getVisibilityIcon(visibility.value),
size: 16, size: 16,
color: color: colorScheme.primary,
Theme.of(context).colorScheme.primary,
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(
getVisibilityText(visibility.value).tr(), getVisibilityText(visibility.value).tr(),
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: color: colorScheme.primary,
Theme.of(context).colorScheme.primary,
), ),
), ),
], ],
@ -508,33 +588,40 @@ class PostComposeScreen extends HookConsumerWidget {
), ),
], ],
).padding(bottom: 6), ).padding(bottom: 6),
// Title field
TextField( TextField(
controller: titleController, controller: titleController,
decoration: InputDecoration.collapsed( decoration: InputDecoration.collapsed(
hintText: 'postTitle'.tr(), hintText: 'postTitle'.tr(),
), ),
style: TextStyle(fontSize: 16), style: const TextStyle(fontSize: 16),
onTapOutside: onTapOutside:
(_) => (_) =>
FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
), ),
// Description field
TextField( TextField(
controller: descriptionController, controller: descriptionController,
decoration: InputDecoration.collapsed( decoration: InputDecoration.collapsed(
hintText: 'postDescription'.tr(), hintText: 'postDescription'.tr(),
), ),
style: TextStyle(fontSize: 16), style: const TextStyle(fontSize: 16),
onTapOutside: onTapOutside:
(_) => (_) =>
FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
), ),
const Gap(8), const Gap(8),
// Content field with keyboard listener
RawKeyboardListener( RawKeyboardListener(
focusNode: FocusNode(), focusNode: FocusNode(),
onKey: handleKeyPress, onKey: handleKeyPress,
child: TextField( child: TextField(
controller: contentController, controller: contentController,
style: TextStyle(fontSize: 14), style: const TextStyle(fontSize: 14),
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: 'postPlaceholder'.tr(), hintText: 'postPlaceholder'.tr(),
@ -547,80 +634,22 @@ class PostComposeScreen extends HookConsumerWidget {
?.unfocus(), ?.unfocus(),
), ),
), ),
const Gap(8), const Gap(8),
// Attachments preview
LayoutBuilder( LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final isWide = isWideScreen(context); final isWide = isWideScreen(context);
return isWide return isWide
? Wrap( ? buildWideAttachmentGrid(
spacing: 8, constraints,
runSpacing: 8, attachments.value,
children: [ attachmentProgress.value,
for (
var idx = 0;
idx < attachments.value.length;
idx++
)
SizedBox(
width: constraints.maxWidth / 2 - 4,
child: AttachmentPreview(
item: attachments.value[idx],
progress:
attachmentProgress.value[idx],
onRequestUpload:
() => uploadAttachment(idx),
onDelete: () => deleteAttachment(idx),
onMove: (delta) {
if (idx + delta < 0 ||
idx + delta >=
attachments.value.length) {
return;
}
final clone = List.of(
attachments.value,
);
clone.insert(
idx + delta,
clone.removeAt(idx),
);
attachments.value = clone;
},
),
),
],
) )
: Column( : buildNarrowAttachmentList(
crossAxisAlignment: CrossAxisAlignment.start, attachments.value,
spacing: 8, attachmentProgress.value,
children: [
for (
var idx = 0;
idx < attachments.value.length;
idx++
)
AttachmentPreview(
item: attachments.value[idx],
progress: attachmentProgress.value[idx],
onRequestUpload:
() => uploadAttachment(idx),
onDelete: () => deleteAttachment(idx),
onMove: (delta) {
if (idx + delta < 0 ||
idx + delta >=
attachments.value.length) {
return;
}
final clone = List.of(
attachments.value,
);
clone.insert(
idx + delta,
clone.removeAt(idx),
);
attachments.value = clone;
},
),
],
); );
}, },
), ),
@ -631,6 +660,8 @@ class PostComposeScreen extends HookConsumerWidget {
], ],
).padding(horizontal: 16), ).padding(horizontal: 16),
), ),
// Bottom toolbar
Material( Material(
elevation: 4, elevation: 4,
child: Row( child: Row(
@ -638,12 +669,12 @@ class PostComposeScreen extends HookConsumerWidget {
IconButton( IconButton(
onPressed: pickPhotoMedia, onPressed: pickPhotoMedia,
icon: const Icon(Symbols.add_a_photo), icon: const Icon(Symbols.add_a_photo),
color: Theme.of(context).colorScheme.primary, color: colorScheme.primary,
), ),
IconButton( IconButton(
onPressed: pickVideoMedia, onPressed: pickVideoMedia,
icon: const Icon(Symbols.videocam), icon: const Icon(Symbols.videocam),
color: Theme.of(context).colorScheme.primary, color: colorScheme.primary,
), ),
], ],
).padding( ).padding(
@ -656,4 +687,31 @@ class PostComposeScreen extends HookConsumerWidget {
), ),
); );
} }
// Helper method to build info banner for replied/forwarded posts
Widget _buildInfoBanner(
BuildContext context,
IconData icon,
String labelKey,
String publisherNick,
) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5),
child: Row(
children: [
Icon(icon, size: 16),
const Gap(8),
Expanded(
child: Text(
'${'labelKey'.tr()}: $publisherNick',
style: Theme.of(context).textTheme.bodySmall,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
} }

View File

@ -51,9 +51,9 @@ class PostListNotifier extends _$PostListNotifier
enum PostItemType { enum PostItemType {
/// Regular post item with user information /// Regular post item with user information
regular, regular,
/// Creator view with analytics and metadata /// Creator view with analytics and metadata
creator creator,
} }
class SliverPostList extends HookConsumerWidget { class SliverPostList extends HookConsumerWidget {
@ -64,9 +64,9 @@ class SliverPostList extends HookConsumerWidget {
final bool isOpenable; final bool isOpenable;
final Function? onRefresh; final Function? onRefresh;
final Function(SnPost)? onUpdate; final Function(SnPost)? onUpdate;
const SliverPostList({ const SliverPostList({
super.key, super.key,
this.pubName, this.pubName,
this.itemType = PostItemType.regular, this.itemType = PostItemType.regular,
this.backgroundColor, this.backgroundColor,
@ -89,20 +89,17 @@ class SliverPostList extends HookConsumerWidget {
if (index == widgetCount - 1) { if (index == widgetCount - 1) {
return endItemView; return endItemView;
} }
final post = data.items[index]; final post = data.items[index];
return Column( return Column(
children: [ children: [_buildPostItem(post), const Divider(height: 1)],
_buildPostItem(post),
const Divider(height: 1),
],
); );
}, },
), ),
); );
} }
Widget _buildPostItem(SnPost post) { Widget _buildPostItem(SnPost post) {
switch (itemType) { switch (itemType) {
case PostItemType.creator: case PostItemType.creator:
@ -115,7 +112,6 @@ class SliverPostList extends HookConsumerWidget {
onUpdate: onUpdate, onUpdate: onUpdate,
); );
case PostItemType.regular: case PostItemType.regular:
default:
return PostItem(item: post); return PostItem(item: post);
} }
} }