Skip to content

Commit

Permalink
added swipe gestures to posts in compact view
Browse files Browse the repository at this point in the history
  • Loading branch information
hjiangsu committed Jun 23, 2023
1 parent 94866f1 commit b462f98
Showing 1 changed file with 192 additions and 81 deletions.
273 changes: 192 additions & 81 deletions lib/community/widgets/post_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,63 +13,155 @@ import 'package:thunder/core/enums/view_mode.dart';
import 'package:thunder/core/models/post_view_media.dart';
import 'package:thunder/post/bloc/post_bloc.dart' as post_bloc; // renamed to prevent clash with VotePostEvent, etc from community_bloc
import 'package:thunder/post/pages/post_page.dart';
import 'package:thunder/post/widgets/comment_card.dart';
import 'package:thunder/shared/icon_text.dart';
import 'package:thunder/shared/media_view.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/utils/date_time.dart';
import 'package:thunder/utils/instance.dart';
import 'package:thunder/utils/numbers.dart';

class PostCard extends StatelessWidget {
class PostCard extends StatefulWidget {
final PostViewMedia postView;
final bool showInstanceName;

const PostCard({super.key, required this.postView, this.showInstanceName = true});

@override
State<PostCard> createState() => _PostCardState();
}

class _PostCardState extends State<PostCard> {
double dismissThreshold = 0;
DismissDirection? dismissDirection;
SwipeAction? swipeAction;

@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);

int? myVote = widget.postView.myVote;
bool saved = widget.postView.saved;
int score = widget.postView.counts.score;

final bool isUserLoggedIn = context.read<AuthBloc>().state.isLoggedIn;
final bool useCompactView = context.read<ThunderBloc>().state.preferences?.getBool('setting_general_use_compact_view') ?? false;

return Column(
children: [
Divider(
height: 1.0,
thickness: 2.0,
color: theme.textTheme.bodyMedium?.color?.withOpacity(0.20),
),
InkWell(
child: useCompactView ? compactPostLayout(context) : comfortablePostLayout(context),
onTap: () async {
AccountBloc accountBloc = context.read<AccountBloc>();
AuthBloc authBloc = context.read<AuthBloc>();
ThunderBloc thunderBloc = context.read<ThunderBloc>();
CommunityBloc communityBloc = BlocProvider.of<CommunityBloc>(context);

await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MultiBlocProvider(
providers: [
BlocProvider.value(value: accountBloc),
BlocProvider.value(value: authBloc),
BlocProvider.value(value: thunderBloc),
BlocProvider.value(value: communityBloc),
BlocProvider(create: (context) => post_bloc.PostBloc()),
],
child: PostPage(postView: postView),
return Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (event) => {},
onPointerUp: (event) {
// Check to see what the swipe action is
if (swipeAction == SwipeAction.upvote) {
// @todo: optimistic update
int vote = myVote == 1 ? 0 : 1;
context.read<CommunityBloc>().add(VotePostEvent(postId: widget.postView.post.id, score: vote));
}

if (swipeAction == SwipeAction.downvote) {
// @todo: optimistic update
int vote = myVote == -1 ? 0 : -1;
context.read<CommunityBloc>().add(VotePostEvent(postId: widget.postView.post.id, score: vote));
}

if (swipeAction == SwipeAction.reply) {}

if (swipeAction == SwipeAction.save) {
context.read<CommunityBloc>().add(SavePostEvent(postId: widget.postView.post.id, save: !saved));
}
},
onPointerCancel: (event) => {},
child: Dismissible(
direction: isUserLoggedIn ? DismissDirection.horizontal : DismissDirection.none,
key: ObjectKey(widget.postView.post.id),
resizeDuration: Duration.zero,
dismissThresholds: const {DismissDirection.endToStart: 1, DismissDirection.startToEnd: 1},
confirmDismiss: (DismissDirection direction) async {
return false;
},
onUpdate: (DismissUpdateDetails details) {
SwipeAction? _swipeAction;
if (details.progress > 0.1 && details.progress < 0.3 && details.direction == DismissDirection.startToEnd) {
_swipeAction = SwipeAction.upvote;
if (swipeAction != _swipeAction) HapticFeedback.mediumImpact();
} else if (details.progress > 0.3 && details.direction == DismissDirection.startToEnd) {
_swipeAction = SwipeAction.downvote;
if (swipeAction != _swipeAction) HapticFeedback.mediumImpact();
} else if (details.progress > 0.1 && details.progress < 0.3 && details.direction == DismissDirection.endToStart) {
_swipeAction = SwipeAction.reply;
if (swipeAction != _swipeAction) HapticFeedback.mediumImpact();
} else if (details.progress > 0.3 && details.direction == DismissDirection.endToStart) {
_swipeAction = SwipeAction.save;
if (swipeAction != _swipeAction) HapticFeedback.mediumImpact();
} else {
_swipeAction = null;
}

setState(() {
dismissThreshold = details.progress;
dismissDirection = details.direction;
swipeAction = _swipeAction;
});
},
background: dismissDirection == DismissDirection.startToEnd
? AnimatedContainer(
alignment: Alignment.centerLeft,
color: dismissThreshold < 0.3 ? Colors.orange.shade700 : Colors.blue.shade700,
duration: const Duration(milliseconds: 200),
child: SizedBox(
width: MediaQuery.of(context).size.width * dismissThreshold,
child: Icon(dismissThreshold < 0.3 ? Icons.north : Icons.south),
),
)
: AnimatedContainer(
alignment: Alignment.centerRight,
color: dismissThreshold < 0.3 ? Colors.green.shade700 : Colors.purple.shade700,
duration: const Duration(milliseconds: 200),
child: SizedBox(
width: MediaQuery.of(context).size.width * dismissThreshold,
child: Icon(dismissThreshold < 0.3 ? Icons.reply : Icons.star_rounded),
),
),
);
if (context.mounted) context.read<CommunityBloc>().add(ForceRefreshEvent());
},
child: Column(
children: [
Divider(
height: 1.0,
thickness: 2.0,
color: theme.textTheme.bodyMedium?.color?.withOpacity(0.20),
),
InkWell(
child: useCompactView ? compactPostLayout(context) : comfortablePostLayout(context),
onTap: () async {
AccountBloc accountBloc = context.read<AccountBloc>();
AuthBloc authBloc = context.read<AuthBloc>();
ThunderBloc thunderBloc = context.read<ThunderBloc>();
CommunityBloc communityBloc = BlocProvider.of<CommunityBloc>(context);

await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MultiBlocProvider(
providers: [
BlocProvider.value(value: accountBloc),
BlocProvider.value(value: authBloc),
BlocProvider.value(value: thunderBloc),
BlocProvider.value(value: communityBloc),
BlocProvider(create: (context) => post_bloc.PostBloc()),
],
child: PostPage(postView: widget.postView),
),
),
);
if (context.mounted) context.read<CommunityBloc>().add(ForceRefreshEvent());
},
),
],
),
],
),
);
}

Widget compactPostLayout(BuildContext context) {
final Post post = postView.post;
final Post post = widget.postView.post;
final ThemeData theme = Theme.of(context);

final bool hideNsfwPreviews = context.read<ThunderBloc>().state.preferences?.getBool('setting_general_hide_nsfw_previews') ?? true;
Expand All @@ -83,7 +175,7 @@ class PostCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
MediaView(
postView: postView,
postView: widget.postView,
showFullHeightImages: false,
hideNsfwPreviews: hideNsfwPreviews,
viewMode: ViewMode.compact,
Expand All @@ -102,7 +194,7 @@ class PostCard extends StatelessWidget {
const SizedBox(height: 4.0),
GestureDetector(
child: Text(
'${postView.community.name}${showInstanceName ? ' · ${fetchInstanceNameFromUrl(postView.community.actorId)}' : ''}',
'${widget.postView.community.name}${widget.showInstanceName ? ' · ${fetchInstanceNameFromUrl(widget.postView.community.actorId)}' : ''}',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.textTheme.bodyMedium?.color?.withOpacity(0.75),
),
Expand All @@ -122,7 +214,7 @@ class PostCard extends StatelessWidget {
}

Widget comfortablePostLayout(BuildContext context) {
final Post post = postView.post;
final Post post = widget.postView.post;
final ThemeData theme = Theme.of(context);

final bool showFullHeightImages = context.read<ThunderBloc>().state.preferences?.getBool('setting_general_show_full_height_images') ?? false;
Expand All @@ -137,7 +229,7 @@ class PostCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MediaView(
postView: postView,
postView: widget.postView,
showFullHeightImages: showFullHeightImages,
hideNsfwPreviews: hideNsfwPreviews,
),
Expand All @@ -152,7 +244,7 @@ class PostCard extends StatelessWidget {
children: [
GestureDetector(
child: Text(
'${postView.community.name}${showInstanceName ? ' · ${fetchInstanceNameFromUrl(postView.community.actorId)}' : ''}',
'${widget.postView.community.name}${widget.showInstanceName ? ' · ${fetchInstanceNameFromUrl(widget.postView.community.actorId)}' : ''}',
style: theme.textTheme.titleSmall?.copyWith(
fontSize: theme.textTheme.titleSmall!.fontSize! * 1.05,
color: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
Expand All @@ -171,7 +263,7 @@ class PostCard extends StatelessWidget {
BlocProvider.value(value: authBloc),
BlocProvider.value(value: thunderBloc),
],
child: CommunityPage(communityId: postView.community.id),
child: CommunityPage(communityId: widget.postView.community.id),
),
),
);
Expand Down Expand Up @@ -203,7 +295,7 @@ class PostCard extends StatelessWidget {
BlocProvider.value(value: authBloc),
BlocProvider.value(value: thunderBloc),
],
child: CommunityPage(communityId: postView.community.id),
child: CommunityPage(communityId: widget.postView.community.id),
),
),
);
Expand All @@ -213,45 +305,64 @@ class PostCard extends StatelessWidget {
Widget postMetadata(BuildContext context) {
final ThemeData theme = Theme.of(context);

final Post post = postView.post;
final Post post = widget.postView.post;
final int? myVote = widget.postView.myVote;
final bool saved = widget.postView.saved;

final bool useCompactView = context.read<ThunderBloc>().state.preferences?.getBool('setting_general_use_compact_view') ?? false;

return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconText(
text: formatNumberToK(postView.counts.score),
icon: Icon(
Icons.arrow_upward,
size: 18.0,
color: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
),
padding: 2.0,
),
const SizedBox(width: 12.0),
IconText(
icon: Icon(
Icons.chat,
size: 17.0,
color: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
),
text: formatNumberToK(postView.counts.comments),
padding: 5.0,
),
const SizedBox(width: 10.0),
IconText(
icon: Icon(
Icons.history_rounded,
size: 19.0,
color: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
),
text: formatTimeToString(dateTime: post.published),
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconText(
text: formatNumberToK(widget.postView.counts.score),
icon: Icon(
Icons.arrow_upward,
size: 18.0,
color: myVote == 1
? Colors.orange
: myVote == -1
? Colors.blue
: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
),
padding: 2.0,
),
const SizedBox(width: 12.0),
IconText(
icon: Icon(
Icons.chat,
size: 17.0,
color: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
),
text: formatNumberToK(widget.postView.counts.comments),
padding: 5.0,
),
const SizedBox(width: 10.0),
IconText(
icon: Icon(
Icons.history_rounded,
size: 19.0,
color: theme.textTheme.titleSmall?.color?.withOpacity(0.75),
),
text: formatTimeToString(dateTime: post.published),
),
const SizedBox(width: 14.0),
if (post.featuredCommunity == true || post.featuredLocal == true)
Icon(
Icons.campaign_rounded,
size: 24.0,
color: Colors.green.shade800,
),
],
),
const SizedBox(width: 14.0),
if (post.featuredCommunity == true || post.featuredLocal == true)
if (useCompactView)
Icon(
Icons.campaign_rounded,
size: 24.0,
color: Colors.green.shade800,
saved ? Icons.star_rounded : null,
color: saved ? Colors.purple : null,
size: 22.0,
),
],
);
Expand All @@ -264,40 +375,40 @@ class PostCard extends StatelessWidget {
final bool showVoteActions = prefs?.getBool('setting_general_show_vote_actions') ?? true;
final bool showSaveAction = prefs?.getBool('setting_general_show_save_action') ?? true;

final Post post = postView.post;
final Post post = widget.postView.post;

return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (showVoteActions)
IconButton(
icon: const Icon(Icons.arrow_upward),
color: postView.myVote == 1 ? Colors.orange : null,
color: widget.postView.myVote == 1 ? Colors.orange : null,
visualDensity: VisualDensity.compact,
onPressed: () {
HapticFeedback.mediumImpact();
context.read<CommunityBloc>().add(VotePostEvent(postId: post.id, score: postView.myVote == 1 ? 0 : 1));
context.read<CommunityBloc>().add(VotePostEvent(postId: post.id, score: widget.postView.myVote == 1 ? 0 : 1));
}),
if (showVoteActions)
IconButton(
icon: const Icon(Icons.arrow_downward),
color: postView.myVote == -1 ? Colors.blue : null,
color: widget.postView.myVote == -1 ? Colors.blue : null,
visualDensity: VisualDensity.compact,
onPressed: () {
HapticFeedback.mediumImpact();
context.read<CommunityBloc>().add(VotePostEvent(postId: post.id, score: postView.myVote == -1 ? 0 : -1));
context.read<CommunityBloc>().add(VotePostEvent(postId: post.id, score: widget.postView.myVote == -1 ? 0 : -1));
},
),
if (showSaveAction)
IconButton(
icon: Icon(postView.saved ? Icons.star_rounded : Icons.star_border_rounded),
color: postView.saved ? Colors.purple : null,
icon: Icon(widget.postView.saved ? Icons.star_rounded : Icons.star_border_rounded),
color: widget.postView.saved ? Colors.purple : null,
visualDensity: VisualDensity.compact,
onPressed: () {
HapticFeedback.mediumImpact();
context.read<CommunityBloc>().add(SavePostEvent(
postId: post.id,
save: postView.saved ? false : true,
save: widget.postView.saved ? false : true,
));
},
),
Expand Down

0 comments on commit b462f98

Please sign in to comment.