Surface/lib/screens/post/post_detail.dart

174 lines
5.9 KiB
Dart
Raw Normal View History

2024-11-10 22:56:09 +08:00
import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
2024-11-11 20:06:00 +08:00
import 'package:gap/gap.dart';
2024-11-10 22:56:09 +08:00
import 'package:go_router/go_router.dart';
2024-11-11 22:43:09 +08:00
import 'package:material_symbols_icons/symbols.dart';
2024-11-10 22:56:09 +08:00
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
2024-11-26 00:00:09 +08:00
import 'package:surface/providers/post.dart';
2024-11-14 22:49:17 +08:00
import 'package:surface/providers/userinfo.dart';
2024-11-10 22:56:09 +08:00
import 'package:surface/types/post.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/loading_indicator.dart';
2025-01-20 14:21:41 +08:00
import 'package:surface/widgets/navigation/app_background.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
2024-11-11 20:06:00 +08:00
import 'package:surface/widgets/post/post_comment_list.dart';
2024-11-10 22:56:09 +08:00
import 'package:surface/widgets/post/post_item.dart';
class PostDetailScreen extends StatefulWidget {
final String slug;
final SnPost? preload;
2025-01-20 14:21:41 +08:00
final Function? onBack;
2025-01-20 14:21:41 +08:00
const PostDetailScreen({super.key, required this.slug, this.preload, this.onBack});
2024-11-10 22:56:09 +08:00
@override
State<PostDetailScreen> createState() => _PostDetailScreenState();
}
class _PostDetailScreenState extends State<PostDetailScreen> {
bool _isBusy = false;
SnPost? _data;
void _fetchPost() async {
setState(() => _isBusy = true);
try {
2024-11-26 00:00:09 +08:00
final pt = context.read<SnPostContentProvider>();
final post = await pt.getPost(widget.slug);
2024-11-10 22:56:09 +08:00
if (!mounted) return;
2024-11-26 00:00:09 +08:00
_data = post;
2024-11-10 22:56:09 +08:00
} catch (err) {
context.showErrorDialog(err);
} finally {
setState(() => _isBusy = false);
}
}
@override
void initState() {
super.initState();
if (widget.preload != null) {
_data = widget.preload;
}
_fetchPost();
}
2024-11-11 22:43:09 +08:00
final GlobalKey<PostCommentSliverListState> _childListKey = GlobalKey();
2024-11-10 22:56:09 +08:00
@override
Widget build(BuildContext context) {
2024-11-14 22:49:17 +08:00
final ua = context.watch<UserProvider>();
final double maxWidth = _data?.type == 'video' ? double.infinity : 640;
2024-11-11 22:43:09 +08:00
2025-01-20 14:21:41 +08:00
return AppBackground(
isRoot: widget.onBack != null,
child: AppScaffold(
2025-01-20 14:21:41 +08:00
appBar: AppBar(
leading: BackButton(
onPressed: () {
if (widget.onBack != null) {
widget.onBack!.call();
}
if (GoRouter.of(context).canPop()) {
GoRouter.of(context).pop(context);
return;
}
GoRouter.of(context).replaceNamed('explore');
},
2024-11-11 20:06:00 +08:00
),
2025-01-20 14:21:41 +08:00
title: _data?.body['title'] != null
? RichText(
textAlign: TextAlign.center,
text: TextSpan(children: [
TextSpan(
text: _data?.body['title'] ?? 'postNoun'.tr(),
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
const TextSpan(text: '\n'),
TextSpan(
text: 'postDetail'.tr(),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).appBarTheme.foregroundColor!,
),
),
]),
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
: Text('postDetail').tr(),
),
body: CustomScrollView(
slivers: [
2024-11-11 20:06:00 +08:00
SliverToBoxAdapter(
2025-01-20 14:21:41 +08:00
child: LoadingIndicator(isActive: _isBusy),
2024-11-11 20:06:00 +08:00
),
2025-01-20 14:21:41 +08:00
if (_data != null)
SliverToBoxAdapter(
child: PostItem(
data: _data!,
maxWidth: maxWidth,
2025-01-20 14:21:41 +08:00
showComments: false,
showFullPost: true,
onChanged: (data) {
setState(() => _data = data);
},
onDeleted: () {
Navigator.pop(context);
},
),
2024-11-24 00:22:08 +08:00
),
if (_data != null && _data!.type != 'video') const SliverToBoxAdapter(child: Divider(height: 1)),
if (_data != null && _data!.type != 'video')
2025-01-20 14:21:41 +08:00
SliverToBoxAdapter(
child: Container(
constraints: BoxConstraints(maxWidth: maxWidth),
2025-01-20 14:21:41 +08:00
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Symbols.comment, size: 24),
const Gap(16),
Text('postCommentsDetailed')
.plural(_data!.metric.replyCount)
.textStyle(Theme.of(context).textTheme.titleLarge!),
],
).padding(horizontal: 20, vertical: 12).center(),
),
),
if (_data != null && ua.isAuthorized && _data!.type != 'video')
2025-01-20 14:21:41 +08:00
SliverToBoxAdapter(
child: PostCommentQuickAction(
parentPost: _data!,
maxWidth: maxWidth,
onPosted: () {
setState(() {
_data = _data!.copyWith(
metric: _data!.metric.copyWith(
replyCount: _data!.metric.replyCount + 1,
),
);
});
_childListKey.currentState!.refresh();
},
),
2025-01-20 14:21:41 +08:00
),
if (_data != null && _data!.type != 'video')
2025-01-20 14:21:41 +08:00
PostCommentSliverList(
key: _childListKey,
2025-02-08 13:27:53 +08:00
parentPost: _data!,
maxWidth: maxWidth,
2025-01-20 14:21:41 +08:00
),
if (_data != null && _data!.type == 'video') SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
2025-01-20 14:21:41 +08:00
],
),
2024-11-10 22:56:09 +08:00
),
);
}
}