Pinned post & Total vote counts

This commit is contained in:
LittleSheep 2024-07-26 18:23:51 +08:00
parent ae87e9ad31
commit 4552dfd3f3
7 changed files with 133 additions and 9 deletions

View File

@ -18,6 +18,7 @@ class Post {
Post? repostTo;
Realm? realm;
DateTime? publishedAt;
DateTime? pinnedAt;
bool? isDraft;
int authorId;
Account author;
@ -39,6 +40,7 @@ class Post {
required this.repostTo,
required this.realm,
required this.publishedAt,
required this.pinnedAt,
required this.isDraft,
required this.authorId,
required this.author,
@ -70,6 +72,9 @@ class Post {
publishedAt: json['published_at'] != null
? DateTime.parse(json['published_at'])
: null,
pinnedAt: json['pinned_at'] != null
? DateTime.parse(json['pinned_at'])
: null,
isDraft: json['is_draft'],
authorId: json['author_id'],
author: Account.fromJson(json['author']),
@ -93,6 +98,7 @@ class Post {
'repost_to': repostTo?.toJson(),
'realm': realm?.toJson(),
'published_at': publishedAt?.toIso8601String(),
'pinned_at': pinnedAt?.toIso8601String(),
'is_draft': isDraft,
'author_id': authorId,
'author': author.toJson(),

View File

@ -6,17 +6,18 @@ import 'package:solian/exts.dart';
import 'package:solian/models/account.dart';
import 'package:solian/models/attachment.dart';
import 'package:solian/models/pagination.dart';
import 'package:solian/models/post.dart';
import 'package:solian/screens/account/notification.dart';
import 'package:solian/services.dart';
import 'package:solian/theme.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/app_bar_leading.dart';
import 'package:solian/widgets/attachments/attachment_list.dart';
import 'package:solian/widgets/current_state_action.dart';
import 'package:solian/widgets/feed/feed_list.dart';
import 'package:solian/widgets/posts/post_list.dart';
import 'package:solian/widgets/sized_container.dart';
import '../../widgets/attachments/attachment_list.dart';
class AccountProfilePage extends StatefulWidget {
final String name;
@ -35,20 +36,44 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
bool _showMature = false;
Account? _userinfo;
List<Post> _pinnedPosts = List.empty();
int _totalUpvote = 0, _totalDownvote = 0;
Future<void> getUserinfo() async {
setState(() => _isBusy = true);
final client = ServiceFinder.configureClient('auth');
final resp = await client.get('/users/${widget.name}');
if (resp.statusCode == 200) {
_userinfo = Account.fromJson(resp.body);
setState(() => _isBusy = false);
} else {
var client = ServiceFinder.configureClient('auth');
var resp = await client.get('/users/${widget.name}');
if (resp.statusCode != 200) {
context.showErrorDialog(resp.bodyString).then((_) {
Navigator.pop(context);
});
} else {
_userinfo = Account.fromJson(resp.body);
}
client = ServiceFinder.configureClient('interactive');
resp = await client.get('/users/${widget.name}');
if (resp.statusCode != 200) {
context.showErrorDialog(resp.bodyString).then((_) {
Navigator.pop(context);
});
} else {
_totalUpvote = resp.body['total_upvote'];
_totalDownvote = resp.body['total_downvote'];
}
resp = await client.get('/users/${widget.name}/pin');
if (resp.statusCode != 200) {
context.showErrorDialog(resp.bodyString).then((_) {
Navigator.pop(context);
});
} else {
_pinnedPosts =
resp.body.map((x) => Post.fromJson(x)).toList().cast<Post>();
}
setState(() => _isBusy = false);
}
@override
@ -144,12 +169,68 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
RefreshIndicator(
onRefresh: () => _postController.reloadAllOver(),
child: CustomScrollView(slivers: [
SliverToBoxAdapter(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text(
'totalUpvote'.tr,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
_totalUpvote.toString(),
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
Column(
children: [
Text(
'totalDownvote'.tr,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
_totalDownvote.toString(),
style: Theme.of(context).textTheme.bodyLarge,
),
],
),
],
).paddingOnly(top: 16, bottom: 12),
),
const SliverToBoxAdapter(
child: Divider(thickness: 0.3, height: 0.3),
),
SliverList.separated(
itemCount: _pinnedPosts.length,
itemBuilder: (context, idx) {
final element = _pinnedPosts[idx];
return Material(
color:
Theme.of(context).colorScheme.surfaceContainerLow,
child: PostListEntryWidget(
item: element,
isClickable: true,
isNestedClickable: true,
isShowEmbed: true,
onUpdate: () {
_postController.reloadAllOver();
},
),
);
},
separatorBuilder: (context, idx) =>
const Divider(thickness: 0.3, height: 0.3),
),
if (_userinfo == null)
const SliverFillRemaining(
child: Center(child: CircularProgressIndicator()),
),
if (_userinfo != null)
FeedListWidget(
isPinned: false,
controller: _postController.pagingController,
),
]),

View File

@ -86,6 +86,11 @@ const messagesEnglish = {
'notifyAllRead': 'Mark all as read',
'notifyEmpty': 'All notifications read',
'notifyEmptyCaption': 'It seems like nothing happened recently',
'totalUpvote': 'Upvote',
'totalDownvote': 'Downvote',
'pinPost': 'Pin this post',
'unpinPost': 'Unpin this post',
'postPinned': 'Pinned',
'postListNews': 'News',
'postListShuffle': 'Random',
'postEditor': 'Create new post',

View File

@ -80,6 +80,11 @@ const simplifiedChineseMessages = {
'notifyAllRead': '已读所有通知',
'notifyEmpty': '通知箱为空',
'notifyEmptyCaption': '看起来最近没发生什么呢',
'totalUpvote': '获顶数',
'totalDownvote': '获踩数',
'pinPost': '置顶本帖',
'unpinPost': '取消置顶本帖',
'postPinned': '已置顶',
'postEditor': '发个帖子',
'articleEditor': '撰写文章',
'articleDetail': '文章详情',

View File

@ -7,6 +7,7 @@ class FeedListWidget extends StatelessWidget {
final bool isShowEmbed;
final bool isClickable;
final bool isNestedClickable;
final bool isPinned;
final PagingController<int, Post> controller;
const FeedListWidget({
@ -15,6 +16,7 @@ class FeedListWidget extends StatelessWidget {
this.isShowEmbed = true,
this.isClickable = true,
this.isNestedClickable = true,
this.isPinned = true,
});
@override
@ -24,6 +26,9 @@ class FeedListWidget extends StatelessWidget {
pagingController: controller,
builderDelegate: PagedChildBuilderDelegate<Post>(
itemBuilder: (context, item, index) {
if (item.pinnedAt != null && !isPinned) {
return const SizedBox();
}
return PostListEntryWidget(
isShowEmbed: isShowEmbed,
isNestedClickable: isNestedClickable,

View File

@ -31,7 +31,8 @@ class _PostActionState extends State<PostAction> {
setState(() => _isBusy = true);
setState(() {
_canModifyContent = auth.userProfile.value!['id'] == widget.item.author.externalId;
_canModifyContent =
auth.userProfile.value!['id'] == widget.item.author.externalId;
_isBusy = false;
});
}
@ -98,6 +99,21 @@ class _PostActionState extends State<PostAction> {
if (_canModifyContent && !widget.noReact)
const Divider(thickness: 0.3, height: 0.3)
.paddingSymmetric(vertical: 16),
if (_canModifyContent)
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Icons.push_pin),
title: Text(
widget.item.pinnedAt == null
? 'pinPost'.tr
: 'unpinPost'.tr,
),
onTap: () async {
final client = Get.find<AuthProvider>().configureClient('interactive');
await client.post('/posts/${widget.item.id}/pin', {});
Navigator.pop(context, true);
},
),
if (_canModifyContent)
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 24),

View File

@ -112,6 +112,12 @@ class _PostItemState extends State<PostItem> {
),
));
}
if (widget.item.pinnedAt != null) {
widgets.add(Text(
'postPinned'.tr,
style: TextStyle(fontSize: 12, color: _unFocusColor),
));
}
if (widgets.isEmpty) {
return const SizedBox();