Post detail

This commit is contained in:
LittleSheep 2024-05-25 13:19:16 +08:00
parent f376603482
commit daee3e8074
8 changed files with 147 additions and 14 deletions

View File

@ -97,6 +97,7 @@ class AuthProvider extends GetConnect {
);
Get.find<AccountProvider>().connect();
Get.find<AccountProvider>().notifyPrefetch();
return credentials!;
}
@ -105,6 +106,8 @@ class AuthProvider extends GetConnect {
_cacheUserProfileResponse = null;
Get.find<AccountProvider>().disconnect();
Get.find<AccountProvider>().notifications.clear();
Get.find<AccountProvider>().notificationUnread.value = 0;
storage.deleteAll();
}

View File

@ -1,11 +1,27 @@
import 'package:get/get.dart';
import 'package:solian/services.dart';
class PostExploreProvider extends GetConnect {
class PostProvider extends GetConnect {
@override
void onInit() {
httpClient.baseUrl = ServiceFinder.services['interactive'];
}
Future<Response> listPost(int page) => get('/api/feed?take=${10}&offset=$page');
Future<Response> listPost(int page) async {
final resp = await get('/api/feed?take=${10}&offset=$page');
if (resp.statusCode != 200) {
throw Exception(resp.body);
}
return resp;
}
Future<Response> getPost(String alias) async {
final resp = await get('/api/posts/$alias');
if (resp.statusCode != 200) {
throw Exception(resp.body);
}
return resp;
}
}

View File

@ -3,6 +3,7 @@ import 'package:solian/screens/account.dart';
import 'package:solian/screens/account/friend.dart';
import 'package:solian/screens/account/personalize.dart';
import 'package:solian/screens/contact.dart';
import 'package:solian/screens/posts/post_detail.dart';
import 'package:solian/screens/social.dart';
import 'package:solian/screens/posts/publish.dart';
import 'package:solian/shells/basic_shell.dart';
@ -32,6 +33,19 @@ abstract class AppRouter {
),
],
),
ShellRoute(
builder: (context, state, child) =>
BasicShell(state: state, child: child),
routes: [
GoRoute(
path: '/posts/:alias',
name: 'postDetail',
builder: (context, state) => PostDetailScreen(
alias: state.pathParameters['alias']!,
),
),
],
),
ShellRoute(
builder: (context, state, child) =>
BasicShell(state: state, child: child),

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:solian/exts.dart';
import 'package:solian/models/post.dart';
import 'package:solian/providers/content/post_explore.dart';
import 'package:solian/widgets/posts/post_item.dart';
class PostDetailScreen extends StatefulWidget {
final String alias;
const PostDetailScreen({super.key, required this.alias});
@override
State<PostDetailScreen> createState() => _PostDetailScreenState();
}
class _PostDetailScreenState extends State<PostDetailScreen> {
Post? item;
Future<Post?> getDetail() async {
final PostProvider provider = Get.find();
try {
final resp = await provider.getPost(widget.alias);
item = Post.fromJson(resp.body);
} catch (e) {
context.showErrorDialog(e).then((_) => Navigator.pop(context));
}
return item;
}
@override
Widget build(BuildContext context) {
return Material(
color: Theme.of(context).colorScheme.surface,
child: FutureBuilder(
future: getDetail(),
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Column(
children: [
PostItem(
item: item!,
isClickable: true,
isShowReply: false,
),
],
);
},
),
);
}
}

View File

@ -23,10 +23,13 @@ class _SocialScreenState extends State<SocialScreen> {
PagingController(firstPageKey: 0);
getPosts(int pageKey) async {
final PostExploreProvider provider = Get.find();
final resp = await provider.listPost(pageKey);
if (resp.statusCode != 200) {
_pagingController.error = resp.bodyString;
final PostProvider provider = Get.find();
Response resp;
try {
resp = await provider.listPost(pageKey);
} catch (e) {
_pagingController.error = e;
return;
}
@ -41,7 +44,7 @@ class _SocialScreenState extends State<SocialScreen> {
@override
void initState() {
Get.lazyPut(() => PostExploreProvider());
Get.lazyPut(() => PostProvider());
super.initState();
_pagingController.addPageRequestListener(getPosts);
@ -104,11 +107,17 @@ class _SocialScreenState extends State<SocialScreen> {
child: PostItem(
key: Key('p${item.alias}'),
item: item,
isClickable: true,
).paddingSymmetric(
vertical:
(item.attachments?.isEmpty ?? false) ? 8 : 0,
),
onTap: () {},
onTap: () {
AppRouter.instance.pushNamed(
'postDetail',
pathParameters: {'alias': item.alias},
);
},
onLongPress: () {
showModalBottomSheet(
useRootNavigator: true,

View File

@ -62,6 +62,7 @@ class SolianMessages extends Translations {
'notifyEmpty': 'All notifications read',
'notifyEmptyCaption': 'It seems like nothing happened recently',
'postAction': 'Post',
'postDetail': 'Post',
'postPublishing': 'Post a post',
'postIdentityNotify': 'You will post this post as',
'postContentPlaceholder': 'What\'s happened?!',
@ -140,6 +141,7 @@ class SolianMessages extends Translations {
'notifyEmpty': '通知箱为空',
'notifyEmptyCaption': '看起来最近没发生什么呢',
'postAction': '发表',
'postDetail': '帖子详情',
'postPublishing': '发表帖子',
'postIdentityNotify': '你将会以本身份发表帖子',
'postContentPlaceholder': '发生什么事了?!',

View File

@ -4,6 +4,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get_utils/get_utils.dart';
import 'package:solian/models/post.dart';
import 'package:solian/router.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
import 'package:solian/widgets/posts/post_quick_action.dart';
@ -11,14 +12,18 @@ import 'package:timeago/timeago.dart' show format;
class PostItem extends StatefulWidget {
final Post item;
final bool isClickable;
final bool isCompact;
final bool isReactable;
final bool isShowReply;
const PostItem({
super.key,
required this.item,
this.isClickable = false,
this.isCompact = false,
this.isReactable = true,
this.isShowReply = true,
});
@override
@ -158,9 +163,31 @@ class _PostItemState extends State<PostItem> {
padding: const EdgeInsets.all(0),
).paddingOnly(left: 12, right: 8),
if (widget.item.replyTo != null)
buildReply(context).paddingOnly(top: 4),
GestureDetector(
child: buildReply(context).paddingOnly(top: 4),
onTap: () {
if (!widget.isClickable) return;
AppRouter.instance.pushNamed(
'postDetail',
pathParameters: {
'alias': widget.item.replyTo!.alias,
},
);
},
),
if (widget.item.repostTo != null)
buildRepost(context).paddingOnly(top: 4),
GestureDetector(
child: buildRepost(context).paddingOnly(top: 4),
onTap: () {
if (!widget.isClickable) return;
AppRouter.instance.pushNamed(
'postDetail',
pathParameters: {
'alias': widget.item.repostTo!.alias,
},
);
},
),
],
),
)
@ -173,6 +200,7 @@ class _PostItemState extends State<PostItem> {
),
AttachmentList(attachmentsId: item.attachments ?? List.empty()),
PostQuickAction(
isShowReply: widget.isShowReply,
isReactable: widget.isReactable,
item: widget.item,
onReact: (symbol, changes) {

View File

@ -10,11 +10,13 @@ import 'package:solian/widgets/posts/post_reaction.dart';
class PostQuickAction extends StatefulWidget {
final Post item;
final bool isReactable;
final bool isShowReply;
final void Function(String symbol, int num) onReact;
const PostQuickAction({
super.key,
required this.item,
this.isShowReply = true,
this.isReactable = true,
required this.onReact,
});
@ -93,17 +95,17 @@ class _PostQuickActionState extends State<PostQuickAction> {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (widget.isReactable)
if (widget.isReactable && widget.isShowReply)
ActionChip(
avatar: const Icon(Icons.comment),
label: Text(widget.item.replyCount.toString()),
visualDensity: density,
onPressed: () {},
),
if (widget.isReactable)
if (widget.isReactable && widget.isShowReply)
const VerticalDivider(
thickness: 0.3, width: 0.3, indent: 8, endIndent: 8)
.paddingOnly(left: 8),
.paddingSymmetric(horizontal: 8),
Expanded(
child: ListView(
shrinkWrap: true,
@ -132,7 +134,7 @@ class _PostQuickActionState extends State<PostQuickAction> {
onPressed: () => showReactMenu(),
),
],
).paddingOnly(left: 8),
),
)
],
),