✨ Pinned post & Total vote counts
This commit is contained in:
parent
ae87e9ad31
commit
4552dfd3f3
@ -18,6 +18,7 @@ class Post {
|
|||||||
Post? repostTo;
|
Post? repostTo;
|
||||||
Realm? realm;
|
Realm? realm;
|
||||||
DateTime? publishedAt;
|
DateTime? publishedAt;
|
||||||
|
DateTime? pinnedAt;
|
||||||
bool? isDraft;
|
bool? isDraft;
|
||||||
int authorId;
|
int authorId;
|
||||||
Account author;
|
Account author;
|
||||||
@ -39,6 +40,7 @@ class Post {
|
|||||||
required this.repostTo,
|
required this.repostTo,
|
||||||
required this.realm,
|
required this.realm,
|
||||||
required this.publishedAt,
|
required this.publishedAt,
|
||||||
|
required this.pinnedAt,
|
||||||
required this.isDraft,
|
required this.isDraft,
|
||||||
required this.authorId,
|
required this.authorId,
|
||||||
required this.author,
|
required this.author,
|
||||||
@ -70,6 +72,9 @@ class Post {
|
|||||||
publishedAt: json['published_at'] != null
|
publishedAt: json['published_at'] != null
|
||||||
? DateTime.parse(json['published_at'])
|
? DateTime.parse(json['published_at'])
|
||||||
: null,
|
: null,
|
||||||
|
pinnedAt: json['pinned_at'] != null
|
||||||
|
? DateTime.parse(json['pinned_at'])
|
||||||
|
: null,
|
||||||
isDraft: json['is_draft'],
|
isDraft: json['is_draft'],
|
||||||
authorId: json['author_id'],
|
authorId: json['author_id'],
|
||||||
author: Account.fromJson(json['author']),
|
author: Account.fromJson(json['author']),
|
||||||
@ -93,6 +98,7 @@ class Post {
|
|||||||
'repost_to': repostTo?.toJson(),
|
'repost_to': repostTo?.toJson(),
|
||||||
'realm': realm?.toJson(),
|
'realm': realm?.toJson(),
|
||||||
'published_at': publishedAt?.toIso8601String(),
|
'published_at': publishedAt?.toIso8601String(),
|
||||||
|
'pinned_at': pinnedAt?.toIso8601String(),
|
||||||
'is_draft': isDraft,
|
'is_draft': isDraft,
|
||||||
'author_id': authorId,
|
'author_id': authorId,
|
||||||
'author': author.toJson(),
|
'author': author.toJson(),
|
||||||
|
@ -6,17 +6,18 @@ import 'package:solian/exts.dart';
|
|||||||
import 'package:solian/models/account.dart';
|
import 'package:solian/models/account.dart';
|
||||||
import 'package:solian/models/attachment.dart';
|
import 'package:solian/models/attachment.dart';
|
||||||
import 'package:solian/models/pagination.dart';
|
import 'package:solian/models/pagination.dart';
|
||||||
|
import 'package:solian/models/post.dart';
|
||||||
import 'package:solian/screens/account/notification.dart';
|
import 'package:solian/screens/account/notification.dart';
|
||||||
import 'package:solian/services.dart';
|
import 'package:solian/services.dart';
|
||||||
import 'package:solian/theme.dart';
|
import 'package:solian/theme.dart';
|
||||||
import 'package:solian/widgets/account/account_avatar.dart';
|
import 'package:solian/widgets/account/account_avatar.dart';
|
||||||
import 'package:solian/widgets/app_bar_leading.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/current_state_action.dart';
|
||||||
import 'package:solian/widgets/feed/feed_list.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 'package:solian/widgets/sized_container.dart';
|
||||||
|
|
||||||
import '../../widgets/attachments/attachment_list.dart';
|
|
||||||
|
|
||||||
class AccountProfilePage extends StatefulWidget {
|
class AccountProfilePage extends StatefulWidget {
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
@ -35,20 +36,44 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
bool _showMature = false;
|
bool _showMature = false;
|
||||||
|
|
||||||
Account? _userinfo;
|
Account? _userinfo;
|
||||||
|
List<Post> _pinnedPosts = List.empty();
|
||||||
|
int _totalUpvote = 0, _totalDownvote = 0;
|
||||||
|
|
||||||
Future<void> getUserinfo() async {
|
Future<void> getUserinfo() async {
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
final client = ServiceFinder.configureClient('auth');
|
var client = ServiceFinder.configureClient('auth');
|
||||||
final resp = await client.get('/users/${widget.name}');
|
var resp = await client.get('/users/${widget.name}');
|
||||||
if (resp.statusCode == 200) {
|
if (resp.statusCode != 200) {
|
||||||
_userinfo = Account.fromJson(resp.body);
|
|
||||||
setState(() => _isBusy = false);
|
|
||||||
} else {
|
|
||||||
context.showErrorDialog(resp.bodyString).then((_) {
|
context.showErrorDialog(resp.bodyString).then((_) {
|
||||||
Navigator.pop(context);
|
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
|
@override
|
||||||
@ -144,12 +169,68 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
RefreshIndicator(
|
RefreshIndicator(
|
||||||
onRefresh: () => _postController.reloadAllOver(),
|
onRefresh: () => _postController.reloadAllOver(),
|
||||||
child: CustomScrollView(slivers: [
|
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)
|
if (_userinfo == null)
|
||||||
const SliverFillRemaining(
|
const SliverFillRemaining(
|
||||||
child: Center(child: CircularProgressIndicator()),
|
child: Center(child: CircularProgressIndicator()),
|
||||||
),
|
),
|
||||||
if (_userinfo != null)
|
if (_userinfo != null)
|
||||||
FeedListWidget(
|
FeedListWidget(
|
||||||
|
isPinned: false,
|
||||||
controller: _postController.pagingController,
|
controller: _postController.pagingController,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
@ -86,6 +86,11 @@ const messagesEnglish = {
|
|||||||
'notifyAllRead': 'Mark all as read',
|
'notifyAllRead': 'Mark all as read',
|
||||||
'notifyEmpty': 'All notifications read',
|
'notifyEmpty': 'All notifications read',
|
||||||
'notifyEmptyCaption': 'It seems like nothing happened recently',
|
'notifyEmptyCaption': 'It seems like nothing happened recently',
|
||||||
|
'totalUpvote': 'Upvote',
|
||||||
|
'totalDownvote': 'Downvote',
|
||||||
|
'pinPost': 'Pin this post',
|
||||||
|
'unpinPost': 'Unpin this post',
|
||||||
|
'postPinned': 'Pinned',
|
||||||
'postListNews': 'News',
|
'postListNews': 'News',
|
||||||
'postListShuffle': 'Random',
|
'postListShuffle': 'Random',
|
||||||
'postEditor': 'Create new post',
|
'postEditor': 'Create new post',
|
||||||
|
@ -80,6 +80,11 @@ const simplifiedChineseMessages = {
|
|||||||
'notifyAllRead': '已读所有通知',
|
'notifyAllRead': '已读所有通知',
|
||||||
'notifyEmpty': '通知箱为空',
|
'notifyEmpty': '通知箱为空',
|
||||||
'notifyEmptyCaption': '看起来最近没发生什么呢',
|
'notifyEmptyCaption': '看起来最近没发生什么呢',
|
||||||
|
'totalUpvote': '获顶数',
|
||||||
|
'totalDownvote': '获踩数',
|
||||||
|
'pinPost': '置顶本帖',
|
||||||
|
'unpinPost': '取消置顶本帖',
|
||||||
|
'postPinned': '已置顶',
|
||||||
'postEditor': '发个帖子',
|
'postEditor': '发个帖子',
|
||||||
'articleEditor': '撰写文章',
|
'articleEditor': '撰写文章',
|
||||||
'articleDetail': '文章详情',
|
'articleDetail': '文章详情',
|
||||||
|
@ -7,6 +7,7 @@ class FeedListWidget extends StatelessWidget {
|
|||||||
final bool isShowEmbed;
|
final bool isShowEmbed;
|
||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
final bool isNestedClickable;
|
final bool isNestedClickable;
|
||||||
|
final bool isPinned;
|
||||||
final PagingController<int, Post> controller;
|
final PagingController<int, Post> controller;
|
||||||
|
|
||||||
const FeedListWidget({
|
const FeedListWidget({
|
||||||
@ -15,6 +16,7 @@ class FeedListWidget extends StatelessWidget {
|
|||||||
this.isShowEmbed = true,
|
this.isShowEmbed = true,
|
||||||
this.isClickable = true,
|
this.isClickable = true,
|
||||||
this.isNestedClickable = true,
|
this.isNestedClickable = true,
|
||||||
|
this.isPinned = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -24,6 +26,9 @@ class FeedListWidget extends StatelessWidget {
|
|||||||
pagingController: controller,
|
pagingController: controller,
|
||||||
builderDelegate: PagedChildBuilderDelegate<Post>(
|
builderDelegate: PagedChildBuilderDelegate<Post>(
|
||||||
itemBuilder: (context, item, index) {
|
itemBuilder: (context, item, index) {
|
||||||
|
if (item.pinnedAt != null && !isPinned) {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
return PostListEntryWidget(
|
return PostListEntryWidget(
|
||||||
isShowEmbed: isShowEmbed,
|
isShowEmbed: isShowEmbed,
|
||||||
isNestedClickable: isNestedClickable,
|
isNestedClickable: isNestedClickable,
|
||||||
|
@ -31,7 +31,8 @@ class _PostActionState extends State<PostAction> {
|
|||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_canModifyContent = auth.userProfile.value!['id'] == widget.item.author.externalId;
|
_canModifyContent =
|
||||||
|
auth.userProfile.value!['id'] == widget.item.author.externalId;
|
||||||
_isBusy = false;
|
_isBusy = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -98,6 +99,21 @@ class _PostActionState extends State<PostAction> {
|
|||||||
if (_canModifyContent && !widget.noReact)
|
if (_canModifyContent && !widget.noReact)
|
||||||
const Divider(thickness: 0.3, height: 0.3)
|
const Divider(thickness: 0.3, height: 0.3)
|
||||||
.paddingSymmetric(vertical: 16),
|
.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)
|
if (_canModifyContent)
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
@ -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) {
|
if (widgets.isEmpty) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
|
Loading…
Reference in New Issue
Block a user