Skip to content

Commit

Permalink
Ability to show/hide post and comment scores; Ability to show/hide bo…
Browse files Browse the repository at this point in the history
…t content (#936)

* added additional lemmy options to show/hide score and bot accounts

* refactored comment header to handle hiding scores, hid post scores in post page

* updated changelog

* minor cleanup and tweaks to post card metadata
  • Loading branch information
hjiangsu authored Dec 4, 2023
1 parent 6238c93 commit 84ac4bf
Show file tree
Hide file tree
Showing 12 changed files with 265 additions and 144 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- Added ability to show/hide read posts in user settings
- Added post and comment previews to settings, and reorganized settings page
- Added setting to show comment score instead of upvote/downvote counts
- Added setting to show/hide post and comment scores
- Added setting to show/hide bot content

### Fixed
- Fixed issue where custom tabs would not respect default browser when opening links
Expand Down
134 changes: 59 additions & 75 deletions lib/community/widgets/post_card_metadata.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lemmy_api_client/v3.dart';

import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/feed/feed.dart';
import 'package:thunder/shared/community_icon.dart';
import 'package:thunder/shared/icon_text.dart';
Expand Down Expand Up @@ -40,86 +41,69 @@ class PostCardMetaData extends StatelessWidget {

@override
Widget build(BuildContext context) {
final AuthState authState = context.watch<AuthBloc>().state;
final showScores = authState.getSiteResponse?.myUser?.localUserView.localUser.showScores ?? true;

return BlocBuilder<ThunderBloc, ThunderState>(
builder: (context, state) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
return Wrap(
children: [
Flexible(
child: Wrap(
alignment: WrapAlignment.start,
runSpacing: 2,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconText(
fontScale: state.metadataFontSizeScale,
text: formatNumberToK(score),
textColor: voteType == 1
? upVoteColor
: voteType == -1
? downVoteColor
: readColor,
icon: Icon(voteType == 1 ? Icons.arrow_upward : (voteType == -1 ? Icons.arrow_downward : (score < 0 ? Icons.arrow_downward : Icons.arrow_upward)),
size: 20.0,
color: voteType == 1
? upVoteColor
: voteType == -1
? downVoteColor
: readColor),
padding: 2.0,
),
const SizedBox(width: 10.0),
IconText(
fontScale: state.metadataFontSizeScale,
icon: Icon(
/*unreadComments != 0 && unreadComments != comments ? Icons.mark_unread_chat_alt_rounded :*/ Icons.chat,
size: 15.0,
color: /*unreadComments != 0 && unreadComments != comments ? theme.primaryColor :*/
readColor,
),
text: /*unreadComments != 0 && unreadComments != comments ? '+${formatNumberToK(unreadComments)}' :*/
formatNumberToK(comments),
textColor: /*unreadComments != 0 && unreadComments != comments ? theme.primaryColor :*/
readColor,
padding: 5.0,
),
const SizedBox(width: 10.0),
IconText(
fontScale: state.metadataFontSizeScale,
icon: Icon(
hasBeenEdited ? Icons.edit : Icons.history_rounded,
size: 15.0,
color: readColor,
),
text: formatTimeToString(dateTime: published.toIso8601String()),
textColor: readColor,
),
const SizedBox(width: 8.0),
],
),
if (hostURL != null)
Padding(
padding: const EdgeInsets.only(left: 2.0),
child: Tooltip(
message: hostURL,
preferBelow: false,
child: IconText(
fontScale: state.metadataFontSizeScale,
icon: Icon(
Icons.public,
size: 15.0,
color: readColor,
),
text: Uri.parse(hostURL!).host.replaceFirst('www.', ''),
textColor: readColor,
),
),
),
],
IconText(
fontScale: state.metadataFontSizeScale,
text: showScores ? formatNumberToK(score) : null,
textColor: voteType == 1
? upVoteColor
: voteType == -1
? downVoteColor
: readColor,
icon: Icon(voteType == 1 ? Icons.arrow_upward : (voteType == -1 ? Icons.arrow_downward : (score < 0 ? Icons.arrow_downward : Icons.arrow_upward)),
size: 20.0,
color: voteType == 1
? upVoteColor
: voteType == -1
? downVoteColor
: readColor),
padding: 2.0,
),
const SizedBox(width: 8.0),
IconText(
fontScale: state.metadataFontSizeScale,
icon: Icon(
Icons.chat,
size: 18.0,
color: readColor,
),
text: formatNumberToK(comments),
textColor: readColor,
padding: 5.0,
),
const SizedBox(width: 8.0),
IconText(
fontScale: state.metadataFontSizeScale,
icon: Icon(
hasBeenEdited ? Icons.edit : Icons.history_rounded,
size: 18.0,
color: readColor,
),
text: formatTimeToString(dateTime: published.toIso8601String()),
textColor: readColor,
),
const SizedBox(width: 8.0),
if (hostURL != null)
Tooltip(
message: hostURL,
preferBelow: false,
child: IconText(
fontScale: state.metadataFontSizeScale,
icon: Icon(
Icons.public,
size: 17.0,
color: readColor,
),
text: Uri.parse(hostURL!).host.replaceFirst('www.', ''),
textColor: readColor,
),
),
],
);
},
Expand Down
13 changes: 13 additions & 0 deletions lib/core/auth/bloc/auth_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,18 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {

return emit(state.copyWith(status: AuthStatus.success, account: account, isLoggedIn: activeProfileId?.isNotEmpty == true, downvotesEnabled: downvotesEnabled, getSiteResponse: getSiteResponse));
});

/// When any account setting synced with Lemmy is updated, re-fetch the instance information and preferences.
on<LemmyAccountSettingUpdated>((event, emit) async {
LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3;

// Check to see if there is an active, non-anonymous account
SharedPreferences prefs = (await UserPreferences.instance).sharedPreferences;
String? activeProfileId = prefs.getString('active_profile_id');
Account? account = (activeProfileId != null) ? await Account.fetchAccount(activeProfileId) : null;

GetSiteResponse getSiteResponse = await lemmy.run(GetSite(auth: account?.jwt));
return emit(state.copyWith(status: AuthStatus.success, account: account, isLoggedIn: activeProfileId?.isNotEmpty == true, getSiteResponse: getSiteResponse));
});
}
}
4 changes: 4 additions & 0 deletions lib/core/auth/bloc/auth_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,7 @@ class InstanceChanged extends AuthEvent {

const InstanceChanged({required this.instance});
}

/// The [LemmyAccountSettingUpdated] event should be triggered whenever the any user Lemmy account setting is updated.
/// This event should handle any logic related to refetching the updated user preferences.
class LemmyAccountSettingUpdated extends AuthEvent {}
8 changes: 8 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@
"@sharePost": {},
"showAll": "Show All",
"@showAll": {},
"showBotAccounts": "Show Bot Accounts",
"@showBotAccounts": {
"description": "Option to show bot accounts."
},
"showLess": "Show less",
"@showLess": {},
"showMore": "Show more",
Expand All @@ -646,6 +650,10 @@
"@showReadPosts": {
"description": "Setting to show read posts in feed."
},
"showScores": "Show Post/Comment Scores",
"@showScores": {
"description": "Option to show scores on posts and comments."
},
"sidebar": "Sidebar",
"@sidebar": {},
"sidebarBottomNavDoubleTapDescription": "Double-tap bottom nav to open sidebar",
Expand Down
31 changes: 19 additions & 12 deletions lib/post/widgets/post_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
final bool isUserLoggedIn = context.watch<AuthBloc>().state.isLoggedIn;
final bool downvotesEnabled = context.read<AuthBloc>().state.downvotesEnabled;
final ThunderState thunderState = context.read<ThunderBloc>().state;
final AuthState authState = context.watch<AuthBloc>().state;

final bool showScores = authState.getSiteResponse?.myUser?.localUserView.localUser.showScores ?? true;

final bool scrapeMissingPreviews = thunderState.scrapeMissingPreviews;
final bool hideNsfwPreviews = thunderState.hideNsfwPreviews;
Expand Down Expand Up @@ -302,13 +305,15 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
semanticLabel: postView.myVote == 1 ? 'Upvoted' : 'Upvote',
color: isUserLoggedIn ? (postView.myVote == 1 ? Colors.orange : theme.textTheme.bodyMedium?.color) : null,
),
const SizedBox(width: 4.0),
Text(
formatNumberToK(widget.postViewMedia.postView.counts.upvotes),
style: TextStyle(
color: isUserLoggedIn ? (postView.myVote == 1 ? Colors.orange : theme.textTheme.bodyMedium?.color) : null,
if (showScores) ...[
const SizedBox(width: 4.0),
Text(
formatNumberToK(widget.postViewMedia.postView.counts.upvotes),
style: TextStyle(
color: isUserLoggedIn ? (postView.myVote == 1 ? Colors.orange : theme.textTheme.bodyMedium?.color) : null,
),
),
),
]
],
),
),
Expand Down Expand Up @@ -336,13 +341,15 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
semanticLabel: postView.myVote == 1 ? 'Downvoted' : 'Downvote',
color: isUserLoggedIn ? (postView.myVote == -1 ? Colors.blue : theme.textTheme.bodyMedium?.color) : null,
),
const SizedBox(width: 4.0),
Text(
formatNumberToK(widget.postViewMedia.postView.counts.downvotes),
style: TextStyle(
color: isUserLoggedIn ? (postView.myVote == -1 ? Colors.blue : theme.textTheme.bodyMedium?.color) : null,
if (showScores) ...[
const SizedBox(width: 4.0),
Text(
formatNumberToK(widget.postViewMedia.postView.counts.downvotes),
style: TextStyle(
color: isUserLoggedIn ? (postView.myVote == -1 ? Colors.blue : theme.textTheme.bodyMedium?.color) : null,
),
),
),
],
],
),
),
Expand Down
38 changes: 33 additions & 5 deletions lib/settings/widgets/toggle_option.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,44 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class ToggleOption extends StatelessWidget {
// Appearance
/// The icon to display when enabled
final IconData? iconEnabled;

/// A custom icon size for the enabled icon if provided
final double? iconEnabledSize;

/// The icon to display when disabled
final IconData? iconDisabled;

// General
/// A custom icon size for the disabled icon if provided
final double? iconDisabledSize;

/// The spacing between the icon and the label. Defaults to 8.0
final double? iconSpacing;

/// The main label for the ToggleOption
final String description;

/// An optional subtitle shown below the description
final String? subtitle;

/// A custom semantic label for the option
final String? semanticLabel;

/// The value of the option.
/// When null, the [Switch] will be hidden and the [onToggle] callback will be ignored.
/// When null, the [onTap] and [onLongPress] callbacks are still available.
final bool? value;

// Callback
/// A callback function to perform when the option is toggled.
/// When null, the [ToggleOption] is non-interactable. No callback functions will be activated.
final Function(bool)? onToggle;

/// A callback function to perform when the option is tapped.
/// If null, tapping will toggle the [Switch] and trigger the [onToggle] callback.
final Function()? onTap;

/// A callback function to perform when the option is long pressed
final Function()? onLongPress;

final List<Widget>? additionalWidgets;
Expand All @@ -26,7 +51,10 @@ class ToggleOption extends StatelessWidget {
this.semanticLabel,
required this.value,
this.iconEnabled,
this.iconEnabledSize,
this.iconDisabled,
this.iconDisabledSize,
this.iconSpacing,
this.onToggle,
this.additionalWidgets,
this.onTap,
Expand Down Expand Up @@ -58,8 +86,8 @@ class ToggleOption extends StatelessWidget {
children: [
Row(
children: [
if (iconEnabled != null && iconDisabled != null) Icon(value == true ? iconEnabled : iconDisabled),
if (iconEnabled != null && iconDisabled != null) const SizedBox(width: 8.0),
if (iconEnabled != null && iconDisabled != null) Icon(value == true ? iconEnabled : iconDisabled, size: value == true ? iconEnabledSize : iconDisabledSize),
if (iconEnabled != null && iconDisabled != null) SizedBox(width: iconSpacing ?? 8.0),
Column(
children: [
ConstrainedBox(
Expand Down
Loading

0 comments on commit 84ac4bf

Please sign in to comment.