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

User and community avatar support in chips #1318

Merged
merged 1 commit into from
Apr 24, 2024
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
5 changes: 5 additions & 0 deletions lib/core/enums/local_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ enum LocalSettings {
name: 'setting_general_post_body_show_user_instance', key: 'postBodyShowUserInstance', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.general),
postBodyShowCommunityInstance(
name: 'setting_general_post_body_show_community_instance', key: 'postBodyShowCommunityInstance', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.general),
postBodyShowCommunityAvatar(
name: 'setting_general_post_body_show_community_avatar', key: 'postBodyShowCommunityAvatar', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.general),

// Advanced Settings
imageCachingMode(name: 'setting_advanced_image_caching_mode', key: 'imageCachingMode', category: LocalSettingsCategories.general, subCategory: LocalSettingsSubCategories.advanced),
Expand All @@ -160,6 +162,7 @@ enum LocalSettings {
showCommentActionButtons(
name: 'setting_general_show_comment_button_actions', key: 'showCommentActionButtons', category: LocalSettingsCategories.comments, subCategory: LocalSettingsSubCategories.general),
commentShowUserInstance(name: 'settings_comment_show_user_instance', key: 'showUserInstance', category: LocalSettingsCategories.comments, subCategory: LocalSettingsSubCategories.comments),
commentShowUserAvatar(name: 'settings_comment_show_user_avatar', key: 'showUserAvatar', category: LocalSettingsCategories.comments, subCategory: LocalSettingsSubCategories.comments),
combineCommentScores(name: 'setting_general_combine_comment_scores', key: 'combineCommentScores', category: LocalSettingsCategories.comments, subCategory: LocalSettingsSubCategories.comments),
nestedCommentIndicatorStyle(
name: 'setting_general_nested_comment_indicator_style', key: 'nestedCommentIndicatorStyle', category: LocalSettingsCategories.comments, subCategory: LocalSettingsSubCategories.comments),
Expand Down Expand Up @@ -347,6 +350,7 @@ extension LocalizationExt on AppLocalizations {
'showCrossPosts': showCrossPosts,
'postBodyShowUserInstance': postBodyShowUserInstance,
'postBodyShowCommunityInstance': postBodyShowCommunityInstance,
'postBodyShowCommunityAvatar': postBodyShowCommunityAvatar,
'keywordFilters': keywordFilters,
'hideTopBarOnScroll': hideTopBarOnScroll,
'compactPostCardMetadataItems': compactPostCardMetadataItems,
Expand All @@ -361,6 +365,7 @@ extension LocalizationExt on AppLocalizations {
'collapseParentCommentBodyOnGesture': collapseParentCommentBodyOnGesture,
'showCommentActionButtons': showCommentActionButtons,
'showUserInstance': showUserInstance,
'showUserAvatar': showUserAvatar,
'combineCommentScores': combineCommentScores,
'nestedCommentIndicatorStyle': nestedCommentIndicatorStyle,
'nestedCommentIndicatorColor': nestedCommentIndicatorColor,
Expand Down
12 changes: 12 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@
"@commentReported": {},
"commentSavedAsDraft": "Comment saved as draft",
"@commentSavedAsDraft": {},
"commentShowUserAvatar": "Show User Avatar",
"@commentShowUserAvatar": {
"description": "Settings toggle to display user avatar alongside their display name in comments"
},
"commentShowUserInstance": "Show User Instance",
"@commentShowUserInstance": {
"description": "Settings toggle to display user instance alongside their display name in comments"
Expand Down Expand Up @@ -1165,6 +1169,10 @@
"@postBodySettingsDescription": {
"description": "Description of post body settings"
},
"postBodyShowCommunityAvatar": "Show Community Avatar",
"@postBodyShowCommunityAvatar": {
"description": "Whether to show the community avatar for post bodies"
},
"postBodyShowCommunityInstance": "Show Community Instance",
"@postBodyShowCommunityInstance": {
"description": "Whether to show the community instance for post bodies"
Expand Down Expand Up @@ -1617,6 +1625,10 @@
"@showUpdateChangelogsSubtitle": {
"description": "Subtitle for setting for showing changelogs after updates"
},
"showUserAvatar": "Show User Avatar",
"@showUserAvatar": {
"description": "Toggle to show user avatar."
},
"showUserDisplayNames": "Show User Display Names",
"@showUserDisplayNames": {
"description": "Toggle to show user display names."
Expand Down
6 changes: 6 additions & 0 deletions lib/post/widgets/post_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import 'package:thunder/post/bloc/post_bloc.dart';
import 'package:thunder/post/cubit/create_post_cubit.dart';
import 'package:thunder/post/widgets/post_metadata.dart';
import 'package:thunder/post/widgets/post_quick_actions_bar.dart';
import 'package:thunder/shared/avatars/community_avatar.dart';
import 'package:thunder/shared/avatars/user_avatar.dart';
import 'package:thunder/shared/chips/community_chip.dart';
import 'package:thunder/shared/chips/user_chip.dart';
import 'package:thunder/shared/common_markdown_body.dart';
Expand Down Expand Up @@ -193,13 +195,16 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
width: MediaQuery.of(context).size.width,
child: Wrap(
alignment: WrapAlignment.spaceBetween,
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 8.0,
children: [
Wrap(
spacing: 6.0,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
UserChip(
personId: postView.creator.id,
personAvatar: UserAvatar(person: postView.creator, radius: 10, thumbnailSize: 20, format: 'png'),
personName: postView.creator.name,
personDisplayName: postView.creator.displayName ?? postView.creator.name,
personUrl: postView.creator.actorId,
Expand All @@ -215,6 +220,7 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
),
CommunityChip(
communityId: postView.community.id,
communityAvatar: CommunityAvatar(community: postView.community, radius: 10, thumbnailSize: 20, format: 'png'),
communityName: postView.community.name,
communityUrl: postView.community.actorId,
),
Expand Down
19 changes: 18 additions & 1 deletion lib/settings/pages/comment_appearance_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class _CommentAppearanceSettingsPageState extends State<CommentAppearanceSetting
/// When toggled on, user instance is displayed alongside the display name/username
bool commentShowUserInstance = false;

/// When toggled on, user avatar is displayed to the left of the display name/username
bool commentShowUserAvatar = false;

/// When toggled on, comment scores will be combined instead of having separate upvotes and downvotes
bool combineCommentScores = false;

Expand All @@ -64,6 +67,7 @@ class _CommentAppearanceSettingsPageState extends State<CommentAppearanceSetting
setState(() {
showCommentButtonActions = prefs.getBool(LocalSettings.showCommentActionButtons.name) ?? false;
commentShowUserInstance = prefs.getBool(LocalSettings.commentShowUserInstance.name) ?? false;
commentShowUserAvatar = prefs.getBool(LocalSettings.commentShowUserAvatar.name) ?? false;
combineCommentScores = prefs.getBool(LocalSettings.combineCommentScores.name) ?? false;
nestedIndicatorStyle = NestedCommentIndicatorStyle.values.byName(prefs.getString(LocalSettings.nestedCommentIndicatorStyle.name) ?? DEFAULT_NESTED_COMMENT_INDICATOR_STYLE.name);
nestedIndicatorColor = NestedCommentIndicatorColor.values.byName(prefs.getString(LocalSettings.nestedCommentIndicatorColor.name) ?? DEFAULT_NESTED_COMMENT_INDICATOR_COLOR.name);
Expand All @@ -84,6 +88,9 @@ class _CommentAppearanceSettingsPageState extends State<CommentAppearanceSetting
case LocalSettings.commentShowUserInstance:
await prefs.setBool(LocalSettings.commentShowUserInstance.name, value);
setState(() => commentShowUserInstance = value);
case LocalSettings.commentShowUserAvatar:
await prefs.setBool(LocalSettings.commentShowUserAvatar.name, value);
setState(() => commentShowUserAvatar = value);
case LocalSettings.combineCommentScores:
await prefs.setBool(LocalSettings.combineCommentScores.name, value);
setState(() => combineCommentScores = value);
Expand Down Expand Up @@ -112,6 +119,7 @@ class _CommentAppearanceSettingsPageState extends State<CommentAppearanceSetting
await prefs.remove(LocalSettings.nestedCommentIndicatorStyle.name);
await prefs.remove(LocalSettings.nestedCommentIndicatorColor.name);
await prefs.remove(LocalSettings.commentShowUserInstance.name);
await prefs.remove(LocalSettings.commentShowUserAvatar.name);

await initPreferences();

Expand Down Expand Up @@ -348,7 +356,16 @@ class _CommentAppearanceSettingsPageState extends State<CommentAppearanceSetting
highlightKey: settingToHighlight == LocalSettings.commentShowUserInstance ? settingToHighlightKey : null,
),
),

SliverToBoxAdapter(
child: ToggleOption(
description: l10n.commentShowUserAvatar,
value: commentShowUserAvatar,
iconEnabled: Icons.account_circle,
iconDisabled: Icons.account_circle_outlined,
onToggle: (bool value) => setPreferences(LocalSettings.commentShowUserAvatar, value),
highlightKey: settingToHighlight == LocalSettings.commentShowUserAvatar ? settingToHighlightKey : null,
),
),
SliverToBoxAdapter(
child: ListOption(
description: l10n.nestedCommentIndicatorStyle,
Expand Down
21 changes: 20 additions & 1 deletion lib/settings/pages/post_appearance_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
/// When enabled, shows the instance of the community in posts
bool postBodyShowCommunityInstance = false;

/// When enabled, shows the avatar of the community in chips
bool postBodyShowCommunityAvatar = false;

/// List of compact post card metadata items to show on the post card
/// The order of the items is important as they will be displayed in that order
List<PostCardMetadataItem> compactPostCardMetadataItems = [];
Expand Down Expand Up @@ -166,6 +169,7 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
postBodyViewType = PostBodyViewType.values.byName(prefs.getString(LocalSettings.postBodyViewType.name) ?? PostBodyViewType.expanded.name);
postBodyShowUserInstance = prefs.getBool(LocalSettings.postBodyShowUserInstance.name) ?? false;
postBodyShowCommunityInstance = prefs.getBool(LocalSettings.postBodyShowCommunityInstance.name) ?? false;
postBodyShowCommunityAvatar = prefs.getBool(LocalSettings.postBodyShowCommunityAvatar.name) ?? false;
});
}

Expand Down Expand Up @@ -274,6 +278,10 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
await prefs.setBool(LocalSettings.postBodyShowCommunityInstance.name, value);
setState(() => postBodyShowCommunityInstance = value);
break;
case LocalSettings.postBodyShowCommunityAvatar:
await prefs.setBool(LocalSettings.postBodyShowCommunityAvatar.name, value);
setState(() => postBodyShowCommunityAvatar = value);
break;
}

if (context.mounted) {
Expand Down Expand Up @@ -310,6 +318,7 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
await prefs.remove(LocalSettings.postBodyViewType.name);
await prefs.remove(LocalSettings.postBodyShowUserInstance.name);
await prefs.remove(LocalSettings.postBodyShowCommunityInstance.name);
await prefs.remove(LocalSettings.postBodyShowCommunityAvatar.name);

await initPreferences();

Expand Down Expand Up @@ -1022,6 +1031,16 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
highlightKey: settingToHighlight == LocalSettings.postBodyShowCommunityInstance ? settingToHighlightKey : null,
),
),
SliverToBoxAdapter(
child: ToggleOption(
description: l10n.postBodyShowCommunityAvatar,
value: postBodyShowCommunityAvatar,
iconEnabled: Icons.image,
iconDisabled: Icons.image_not_supported,
onToggle: (bool value) => setPreferences(LocalSettings.postBodyShowCommunityAvatar, value),
highlightKey: settingToHighlight == LocalSettings.postBodyShowCommunityAvatar ? settingToHighlightKey : null,
),
),
const SliverToBoxAdapter(child: SizedBox(height: 128.0)),
],
),
Expand Down Expand Up @@ -1295,7 +1314,7 @@ class PostCardMetadataDraggableTarget extends StatelessWidget {
),
),
onLeave: (data) => HapticFeedback.mediumImpact(),
onWillAccept: (data) {
onWillAcceptWithDetails: (data) {
if (!containedPostCardMetadataItems.contains(data)) {
return true;
}
Expand Down
17 changes: 15 additions & 2 deletions lib/shared/avatars/community_avatar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ class CommunityAvatar extends StatelessWidget {
/// Whether to show the community status (locked)
final bool showCommunityStatus;

const CommunityAvatar({super.key, this.community, this.radius = 12.0, this.showCommunityStatus = false});
/// The size of the thumbnail's height
final int? thumbnailSize;

/// The image format to request from the instance
final String? format;

const CommunityAvatar({super.key, this.community, this.radius = 12.0, this.showCommunityStatus = false, this.thumbnailSize, this.format});

@override
Widget build(BuildContext context) {
Expand All @@ -41,8 +47,15 @@ class CommunityAvatar extends StatelessWidget {

if (community?.icon?.isNotEmpty != true) return placeholderIcon;

Uri imageUri = Uri.parse(community!.icon!);
bool isPictrsImageEndpoint = imageUri.toString().contains('/pictrs/image/');
Map<String, dynamic> queryParameters = {};
if (isPictrsImageEndpoint && thumbnailSize != null) queryParameters['thumbnail'] = thumbnailSize.toString();
if (isPictrsImageEndpoint && format != null) queryParameters['format'] = format;
Uri thumbnailUri = Uri.https(imageUri.host, imageUri.path, queryParameters);

return CachedNetworkImage(
imageUrl: community!.icon!,
imageUrl: thumbnailUri.toString(),
imageBuilder: (context, imageProvider) {
return Stack(
children: [
Expand Down
17 changes: 15 additions & 2 deletions lib/shared/avatars/user_avatar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ class UserAvatar extends StatelessWidget {
/// The radius of the avatar. Defaults to 16
final double radius;

const UserAvatar({super.key, this.person, this.radius = 16.0});
/// The size of the thumbnail's height
final int? thumbnailSize;

/// The image format to request from the instance
final String? format;

const UserAvatar({super.key, this.person, this.radius = 16.0, this.thumbnailSize, this.format});

@override
Widget build(BuildContext context) {
Expand All @@ -36,8 +42,15 @@ class UserAvatar extends StatelessWidget {

if (person?.avatar?.isNotEmpty != true) return placeholderIcon;

Uri imageUri = Uri.parse(person!.avatar!);
bool isPictrsImageEndpoint = imageUri.toString().contains('/pictrs/image/');
Map<String, dynamic> queryParameters = {};
if (isPictrsImageEndpoint && thumbnailSize != null) queryParameters['thumbnail'] = thumbnailSize.toString();
if (isPictrsImageEndpoint && format != null) queryParameters['format'] = format;
Uri thumbnailUri = Uri.https(imageUri.host, imageUri.path, queryParameters);

return CachedNetworkImage(
imageUrl: person!.avatar!,
imageUrl: thumbnailUri.toString(),
imageBuilder: (context, imageProvider) {
return CircleAvatar(
backgroundColor: Colors.transparent,
Expand Down
27 changes: 20 additions & 7 deletions lib/shared/chips/community_chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:thunder/core/enums/full_name.dart';
import 'package:thunder/feed/utils/utils.dart';
import 'package:thunder/feed/view/feed_page.dart';
import 'package:thunder/shared/avatars/community_avatar.dart';
import 'package:thunder/shared/full_name_widgets.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/utils/instance.dart';
Expand All @@ -15,13 +16,17 @@ class CommunityChip extends StatelessWidget {
const CommunityChip({
super.key,
this.communityId,
this.communityAvatar,
this.communityName,
this.communityUrl,
});

/// The ID of the community.
final int? communityId;

/// The avatar of the community.
final CommunityAvatar? communityAvatar;

/// The name of the community.
final String? communityName;

Expand All @@ -31,6 +36,7 @@ class CommunityChip extends StatelessWidget {
@override
Widget build(BuildContext context) {
final state = context.read<ThunderBloc>().state;
final showCommunityAvatar = state.postBodyShowCommunityAvatar;

return InkWell(
borderRadius: BorderRadius.circular(5),
Expand All @@ -39,13 +45,20 @@ class CommunityChip extends StatelessWidget {
excludeFromSemantics: true,
message: generateCommunityFullName(context, communityName, fetchInstanceNameFromUrl(communityUrl) ?? '-'),
preferBelow: false,
child: CommunityFullNameWidget(
context,
communityName,
fetchInstanceNameFromUrl(communityUrl),
includeInstance: state.postBodyShowCommunityInstance,
fontScale: state.metadataFontSizeScale,
transformColor: (color) => color?.withOpacity(0.75),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (showCommunityAvatar && communityAvatar != null) Padding(padding: const EdgeInsets.only(top: 3, bottom: 3, right: 3), child: communityAvatar!),
CommunityFullNameWidget(
context,
communityName,
fetchInstanceNameFromUrl(communityUrl),
includeInstance: state.postBodyShowCommunityInstance,
fontScale: state.metadataFontSizeScale,
transformColor: (color) => color?.withOpacity(0.75),
),
],
),
),
);
Expand Down
7 changes: 7 additions & 0 deletions lib/shared/chips/user_chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:thunder/core/enums/full_name.dart';
import 'package:thunder/core/enums/user_type.dart';
import 'package:thunder/feed/utils/utils.dart';
import 'package:thunder/feed/view/feed_page.dart';
import 'package:thunder/shared/avatars/user_avatar.dart';
import 'package:thunder/shared/full_name_widgets.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
import 'package:thunder/thunder/thunder_icons.dart';
Expand All @@ -19,6 +20,7 @@ class UserChip extends StatelessWidget {
const UserChip({
super.key,
this.personId,
this.personAvatar,
this.personName,
this.personDisplayName,
this.personUrl,
Expand All @@ -31,6 +33,9 @@ class UserChip extends StatelessWidget {
/// The ID of the user
final int? personId;

/// The avatar of the user
final UserAvatar? personAvatar;

/// The username of the user
final String? personName;

Expand All @@ -57,6 +62,7 @@ class UserChip extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final state = context.read<ThunderBloc>().state;
final showUserAvatar = state.commentShowUserAvatar;

return IgnorePointer(
ignoring: ignorePointerEvents,
Expand All @@ -77,6 +83,7 @@ class UserChip extends StatelessWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (showUserAvatar && personAvatar != null) Padding(padding: const EdgeInsets.only(top: 3, bottom: 3, right: 3), child: personAvatar!),
UserFullNameWidget(
context,
personDisplayName != null && state.useDisplayNames ? personDisplayName! : personName,
Expand Down
Loading
Loading