Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve comment/post actions #958

Merged
merged 1 commit into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
- Added the ability to render SVGs in markdown bodies - contribution from @micahmo
- Added Safari extension to open Lemmy links in Thunder

## Changed
- Added new items to the Post and Comment actions sheet - contribution from @micahmo
- Added ability to block instance from instance page - contribution from @micahmo

### Fixed
- Fixed issue where custom tabs would not respect default browser when opening links
- Fixed issue where initial app startup takes a long time to load (503 errors)
Expand Down
12 changes: 11 additions & 1 deletion lib/community/utils/post_card_action_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'package:thunder/shared/advanced_share_sheet.dart';
import 'package:thunder/shared/picker_item.dart';
import 'package:thunder/shared/snackbar.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/user/bloc/user_bloc.dart';
import 'package:thunder/utils/instance.dart';
import 'package:thunder/utils/navigate_instance.dart';
import 'package:thunder/utils/navigate_user.dart';
Expand All @@ -44,6 +45,7 @@ enum PostCardAction {
save,
toggleRead,
share,
blockUser,
}

class ExtendedPostCardActions {
Expand Down Expand Up @@ -74,6 +76,11 @@ final List<ExtendedPostCardActions> postCardActionItems = [
icon: Icons.person_search_rounded,
label: AppLocalizations.of(GlobalContext.context)!.visitUserProfile,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.blockUser,
icon: Icons.block,
label: AppLocalizations.of(GlobalContext.context)!.blockUser,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.visitCommunity,
icon: Icons.home_work_rounded,
Expand Down Expand Up @@ -253,7 +260,7 @@ void onSelected(BuildContext context, PostCardAction postCardAction, PostViewMed
navigateToUserPage(context, userId: postViewMedia.postView.post.creatorId);
break;
case PostCardAction.visitInstance:
navigateToInstancePage(context, instanceHost: fetchInstanceNameFromUrl(postViewMedia.postView.community.actorId)!);
navigateToInstancePage(context, instanceHost: fetchInstanceNameFromUrl(postViewMedia.postView.community.actorId)!, instanceId: postViewMedia.postView.community.instanceId);
break;
case PostCardAction.sharePost:
Share.share(postViewMedia.postView.post.apId);
Expand Down Expand Up @@ -321,5 +328,8 @@ void onSelected(BuildContext context, PostCardAction postCardAction, PostViewMed
actionsToInclude: [PostCardAction.sharePost, PostCardAction.shareMedia, PostCardAction.shareLink],
);
break;
case PostCardAction.blockUser:
context.read<UserBloc>().add(BlockUserEvent(personId: postViewMedia.postView.creator.id, blocked: true));
break;
}
}
1 change: 1 addition & 0 deletions lib/community/widgets/post_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ class _PostCardState extends State<PostCard> {
actionsToInclude: [
PostCardAction.visitInstance,
PostCardAction.visitProfile,
PostCardAction.blockUser,
PostCardAction.blockInstance,
PostCardAction.visitCommunity,
PostCardAction.blockCommunity,
Expand Down
1 change: 1 addition & 0 deletions lib/community/widgets/post_card_view_comfortable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ class PostCardViewComfortable extends StatelessWidget {
actionsToInclude: [
PostCardAction.visitInstance,
PostCardAction.visitProfile,
PostCardAction.blockUser,
PostCardAction.blockInstance,
PostCardAction.visitCommunity,
PostCardAction.blockCommunity,
Expand Down
11 changes: 11 additions & 0 deletions lib/feed/view/feed_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'package:thunder/shared/common_markdown_body.dart';
import 'package:thunder/shared/snackbar.dart';
import 'package:thunder/shared/text/scalable_text.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/user/bloc/user_bloc.dart';
import 'package:thunder/utils/cache.dart';

enum FeedType { community, user, general }
Expand Down Expand Up @@ -219,6 +220,7 @@ class _FeedViewState extends State<FeedView> {
Widget build(BuildContext context) {
ThunderBloc thunderBloc = context.watch<ThunderBloc>();
bool tabletMode = thunderBloc.state.tabletMode;
final AppLocalizations l10n = AppLocalizations.of(context)!;

return MultiBlocListener(
listeners: [
Expand All @@ -229,6 +231,15 @@ class _FeedViewState extends State<FeedView> {
}
},
),
BlocListener<UserBloc, UserState>(
listener: (context, state) {
if ((state.status == UserStatus.failure || state.status == UserStatus.failedToBlock) && state.errorMessage != null) {
showSnackbar(context, state.errorMessage!);
} else if (state.status == UserStatus.success && state.blockedPerson != null) {
showSnackbar(context, l10n.successfullyBlocked);
}
},
),
BlocListener<InstanceBloc, InstanceState>(
listener: (context, state) {
if (state.message != null) {
Expand Down
107 changes: 81 additions & 26 deletions lib/instance/instance_page.dart
Original file line number Diff line number Diff line change
@@ -1,57 +1,112 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:thunder/core/singletons/lemmy_client.dart';
import 'package:thunder/instance/bloc/instance_bloc.dart';
import 'package:thunder/instance/enums/instance_action.dart';
import 'package:thunder/instance/instance_view.dart';
import 'package:thunder/shared/snackbar.dart';
import 'package:thunder/utils/instance.dart';
import 'package:thunder/utils/links.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class InstancePage extends StatefulWidget {
final Site site;
final bool? isBlocked;

// This is needed (in addition to Site) specifically for blocking.
// Since site is requested directly from the target instance, its ID is only right on its own server
// But it's wrong on the server we're connected to.
final int? instanceId;

const InstancePage({
super.key,
required this.site,
required this.isBlocked,
required this.instanceId,
});

@override
State<InstancePage> createState() => _InstancePageState();
}

class _InstancePageState extends State<InstancePage> {
final GlobalKey<ScaffoldMessengerState> _key = GlobalKey<ScaffoldMessengerState>();
bool? isBlocked;
bool currentlyTogglingBlock = false;

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

return Container(
color: theme.colorScheme.background,
child: SafeArea(
top: false,
child: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
title: Text(fetchInstanceNameFromUrl(widget.site.actorId) ?? ''),
actions: [
IconButton(
tooltip: l10n.openInBrowser,
onPressed: () => handleLink(context, url: widget.site.actorId),
icon: Icon(
Icons.open_in_browser_rounded,
semanticLabel: l10n.openInBrowser,
isBlocked ??= widget.isBlocked ?? false;

return BlocListener<InstanceBloc, InstanceState>(
listener: (context, state) {
if (state.message != null) {
showSnackbar(context, state.message!, customState: _key.currentState);
}

if (state.status == InstanceStatus.success && currentlyTogglingBlock) {
currentlyTogglingBlock = false;
setState(() {
isBlocked = !isBlocked!;
});
}
},
child: ScaffoldMessenger(
key: _key,
child: Scaffold(
body: Container(
color: theme.colorScheme.background,
child: SafeArea(
top: false,
child: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
title: Text(fetchInstanceNameFromUrl(widget.site.actorId) ?? ''),
actions: [
if (LemmyClient.instance.supportsFeature(LemmyFeature.blockInstance) && widget.instanceId != null)
IconButton(
tooltip: isBlocked! ? l10n.unblockInstance : l10n.blockInstance,
onPressed: () {
currentlyTogglingBlock = true;
context.read<InstanceBloc>().add(InstanceActionEvent(
instanceAction: InstanceAction.block,
instanceId: widget.instanceId!,
domain: fetchInstanceNameFromUrl(widget.site.actorId),
value: !isBlocked!,
));
},
icon: Icon(
isBlocked! ? Icons.undo_rounded : Icons.block,
semanticLabel: isBlocked! ? l10n.unblockInstance : l10n.blockInstance,
),
),
IconButton(
tooltip: l10n.openInBrowser,
onPressed: () => handleLink(context, url: widget.site.actorId),
icon: Icon(
Icons.open_in_browser_rounded,
semanticLabel: l10n.openInBrowser,
),
),
],
),
),
],
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20),
child: Material(
child: InstanceView(site: widget.site),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20),
child: Material(
child: InstanceView(site: widget.site),
),
),
),
],
),
),
],
),
),
),
);
Expand Down
4 changes: 4 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,10 @@
"@unableToLoadPostsFrominstance": {},
"unableToLoadReplies": "Unable to load more replies.",
"@unableToLoadReplies": {},
"unblockInstance": "Unblock Instance",
"@unblockInstance": {
"description": "Tooltip for unblocking an instance"
},
"unexpectedError": "Unexpected Error",
"@unexpectedError": {},
"unsubscribe": "Unsubscribe",
Expand Down
5 changes: 5 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import 'package:thunder/core/singletons/database.dart';
import 'package:thunder/core/theme/bloc/theme_bloc.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/thunder/thunder.dart';
import 'package:thunder/user/bloc/user_bloc.dart';
import 'package:thunder/utils/global_context.dart';
import 'package:flutter/foundation.dart';

Expand Down Expand Up @@ -82,6 +83,10 @@ class ThunderApp extends StatelessWidget {
BlocProvider(
create: (context) => InstanceBloc(lemmyClient: LemmyClient.instance),
),
// Used for global user events like block/unblock
BlocProvider(
create: (context) => UserBloc(),
),
],
child: BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
Expand Down
64 changes: 59 additions & 5 deletions lib/post/utils/comment_action_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:share_plus/share_plus.dart';
import 'package:thunder/core/singletons/lemmy_client.dart';
import 'package:thunder/instance/bloc/instance_bloc.dart';
import 'package:thunder/instance/enums/instance_action.dart';
import 'package:thunder/post/bloc/post_bloc.dart';
import 'package:thunder/post/widgets/report_comment_dialog.dart';
import 'package:thunder/shared/multi_picker_item.dart';
import 'package:thunder/shared/picker_item.dart';
import 'package:thunder/shared/snackbar.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:thunder/user/bloc/user_bloc.dart';
import 'package:thunder/utils/global_context.dart';
import 'package:thunder/utils/instance.dart';
import 'package:thunder/utils/navigate_instance.dart';
import 'package:thunder/utils/navigate_user.dart';

import '../../core/auth/bloc/auth_bloc.dart';

Expand All @@ -23,6 +30,10 @@ enum CommentCardAction {
reply,
edit,
report,
visitProfile,
blockUser,
visitInstance,
blockInstance,
}

class ExtendedCommentCardActions {
Expand All @@ -48,16 +59,33 @@ class ExtendedCommentCardActions {
}

final List<ExtendedCommentCardActions> commentCardDefaultActionItems = [
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.visitProfile,
icon: Icons.person_search_rounded,
label: AppLocalizations.of(GlobalContext.context)!.visitUserProfile,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.blockUser,
icon: Icons.block,
label: AppLocalizations.of(GlobalContext.context)!.blockUser,
shouldEnable: (isUserLoggedIn) => isUserLoggedIn,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.visitInstance,
icon: Icons.language,
label: AppLocalizations.of(GlobalContext.context)!.visitInstance,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.blockInstance,
icon: Icons.block_rounded,
label: AppLocalizations.of(GlobalContext.context)!.blockInstance,
shouldEnable: (isUserLoggedIn) => isUserLoggedIn,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.copyText,
icon: Icons.copy_rounded,
label: AppLocalizations.of(GlobalContext.context)!.copyText,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.shareLink,
icon: Icons.share_rounded,
label: AppLocalizations.of(GlobalContext.context)!.shareLink,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.report,
icon: Icons.report_outlined,
Expand Down Expand Up @@ -106,6 +134,11 @@ final List<ExtendedCommentCardActions> commentCardDefaultMultiActionItems = [
shouldShow: (context, commentView) => commentView.creator.id == context.read<AuthBloc>().state.account?.userId,
shouldEnable: (isUserLoggedIn) => isUserLoggedIn,
),
ExtendedCommentCardActions(
commentCardAction: CommentCardAction.shareLink,
icon: Icons.share_rounded,
label: AppLocalizations.of(GlobalContext.context)!.shareLink,
),
];

void showCommentActionBottomModalSheet(
Expand All @@ -114,6 +147,10 @@ void showCommentActionBottomModalSheet(
final bool isUserLoggedIn = context.read<AuthBloc>().state.isLoggedIn;
List<ExtendedCommentCardActions> commentCardActionItems = _updateDefaultCommentActionItems(context, commentView);

if (commentCardActionItems.any((c) => c.commentCardAction == CommentCardAction.blockInstance) && !LemmyClient.instance.supportsFeature(LemmyFeature.blockInstance)) {
commentCardActionItems.removeWhere((c) => c.commentCardAction == CommentCardAction.blockInstance);
}

showModalBottomSheet<void>(
showDragHandle: true,
isScrollControlled: true,
Expand Down Expand Up @@ -232,6 +269,23 @@ void onSelected(
case CommentCardAction.report:
onReportAction(commentView.comment.id);
break;
case CommentCardAction.visitProfile:
navigateToUserPage(context, userId: commentView.creator.id);
break;
case CommentCardAction.blockUser:
context.read<UserBloc>().add(BlockUserEvent(personId: commentView.creator.id, blocked: true));
break;
case CommentCardAction.visitInstance:
navigateToInstancePage(context, instanceHost: fetchInstanceNameFromUrl(commentView.creator.actorId)!, instanceId: commentView.community.instanceId);
break;
case CommentCardAction.blockInstance:
context.read<InstanceBloc>().add(InstanceActionEvent(
instanceAction: InstanceAction.block,
instanceId: commentView.creator.instanceId,
domain: fetchInstanceNameFromUrl(commentView.creator.actorId),
value: true,
));
break;
}
}

Expand Down
Loading