Skip to content
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
19 changes: 14 additions & 5 deletions lib/packages/ui/src/widgets/actions/bottom_sheet_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import 'package:flutter/material.dart';
///
/// When tapped, will call the [onTap] callback.
class BottomSheetAction extends StatelessWidget {
const BottomSheetAction({super.key, required this.leading, this.trailing, required this.title, this.subtitle, required this.onTap});
const BottomSheetAction({
super.key,
required this.leading,
this.trailing,
required this.title,
this.subtitle,
required this.onTap,
this.onLongPress,
});

/// The leading widget
final Widget leading;
Expand All @@ -21,20 +29,21 @@ class BottomSheetAction extends StatelessWidget {
/// Callback function to be called when the category is tapped
final Function() onTap;

/// Callback function to be called when the category is long pressed
final Function()? onLongPress;

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

return InkWell(
onTap: onTap,
onLongPress: onLongPress,
customBorder: const StadiumBorder(),
child: ListTile(
leading: leading,
trailing: trailing,
title: Text(
title,
style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
),
title: Text(title, style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500)),
subtitle: subtitle != null
? Text(
subtitle ?? '',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:thunder/src/features/post/post.dart';
import 'package:thunder/src/features/settings/api.dart';
import 'package:thunder/src/foundation/config/global_context.dart';
import 'package:thunder/src/features/instance/domain/utils/instance_link_utils.dart';
import 'package:thunder/src/shared/full_name_copy_utils.dart';
import 'package:thunder/packages/ui/ui.dart' show BottomSheetAction, MultiPickerItem, PickerItemData;

/// Defines the general actions that can be taken on a comment
Expand Down Expand Up @@ -289,6 +290,15 @@ class _GeneralCommentActionBottomSheetPageState extends State<GeneralCommentActi
title: page.name,
subtitle: generateSubtitle(page),
onTap: () => widget.onSwitchActivePage(page),
onLongPress: switch (page) {
GeneralCommentAction.user => () => copyActivityPubFullName(
type: ActivityPubFullNameType.user,
name: widget.comment.creator?.name,
displayName: widget.comment.creator?.displayName,
instance: fetchInstanceNameFromUrl(widget.comment.creator?.actorId),
),
_ => null,
},
),
),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:thunder/src/features/post/post.dart';
import 'package:thunder/src/features/settings/api.dart';
import 'package:thunder/src/foundation/config/global_context.dart';
import 'package:thunder/src/features/instance/domain/utils/instance_link_utils.dart';
import 'package:thunder/src/shared/full_name_copy_utils.dart';
import 'package:thunder/packages/ui/ui.dart' show BottomSheetAction, MultiPickerItem, PickerItemData;

/// Defines the general actions that can be taken on a post
Expand Down Expand Up @@ -302,6 +303,21 @@ class _GeneralPostActionBottomSheetPageState extends State<GeneralPostActionBott
title: page.name,
subtitle: generateSubtitle(page),
onTap: () => widget.onSwitchActivePage(page),
onLongPress: switch (page) {
GeneralPostAction.user => () => copyActivityPubFullName(
type: ActivityPubFullNameType.user,
name: widget.post.creator?.name,
displayName: widget.post.creator?.displayName,
instance: fetchInstanceNameFromUrl(widget.post.creator?.actorId),
),
GeneralPostAction.community => () => copyActivityPubFullName(
type: ActivityPubFullNameType.community,
name: widget.post.community?.name,
displayName: widget.post.community?.title,
instance: fetchInstanceNameFromUrl(widget.post.community?.actorId),
),
_ => null,
},
),
),
],
Expand Down
63 changes: 63 additions & 0 deletions lib/src/shared/full_name_copy_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import 'package:flutter/services.dart';

import 'package:thunder/src/foundation/config/global_context.dart';
import 'package:thunder/src/features/settings/api.dart';
import 'package:thunder/packages/ui/ui.dart' show showSnackbar;

enum ActivityPubFullNameType {
user,
community,
}

/// Generates the full name of the given type.
///
/// For users, the full name is !name@instance.tld
/// For communities, the full name is !name@instance.tld
String? generateActivityPubFullName({
required ActivityPubFullNameType type,
required String? name,
required String? displayName,
required String? instance,
}) {
if (name == null || name.isEmpty || instance == null || instance.isEmpty) return null;

return switch (type) {
ActivityPubFullNameType.user => generateUserFullName(
null,
name,
displayName,
instance,
userSeparator: FullNameSeparator.lemmy,
useDisplayName: false,
),
ActivityPubFullNameType.community => generateCommunityFullName(
null,
name,
displayName,
instance,
communitySeparator: FullNameSeparator.lemmy,
useDisplayName: false,
),
};
}

/// Copies the full name of the given type to the clipboard.
Future<void> copyActivityPubFullName({
required ActivityPubFullNameType type,
required String? name,
required String? displayName,
required String? instance,
}) async {
final fullName = generateActivityPubFullName(
type: type,
name: name,
displayName: displayName,
instance: instance,
);

if (fullName == null || fullName.isEmpty) return;

HapticFeedback.mediumImpact();
await Clipboard.setData(ClipboardData(text: fullName));
showSnackbar(GlobalContext.l10n.copiedToClipboard);
}
8 changes: 8 additions & 0 deletions lib/src/shared/widgets/chips/community_chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:thunder/src/features/identity/presentation/widgets/full_name_wid
import 'package:thunder/src/features/settings/api.dart';
import 'package:thunder/src/features/instance/domain/utils/instance_link_utils.dart';
import 'package:thunder/src/app/shell/navigation/navigation_utils.dart';
import 'package:thunder/src/shared/full_name_copy_utils.dart';

/// A chip which displays the given community and instance information.
///
Expand Down Expand Up @@ -52,6 +53,13 @@ class CommunityChip extends StatelessWidget {
onTap: () => navigateToFeedPage(context, feedType: FeedType.community, communityId: communityId),
child: Tooltip(
excludeFromSemantics: true,
triggerMode: TooltipTriggerMode.longPress,
onTriggered: () => copyActivityPubFullName(
type: ActivityPubFullNameType.community,
name: communityName,
displayName: communityTitle,
instance: fetchInstanceNameFromUrl(communityUrl),
),
message: generateCommunityFullName(
context,
communityName,
Expand Down
8 changes: 8 additions & 0 deletions lib/src/shared/widgets/chips/user_chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:thunder/src/features/settings/api.dart';
import 'package:thunder/src/features/user/api.dart';
import 'package:thunder/src/features/instance/domain/utils/instance_link_utils.dart';
import 'package:thunder/src/app/shell/navigation/navigation_utils.dart';
import 'package:thunder/src/shared/full_name_copy_utils.dart';
import 'package:thunder/packages/ui/ui.dart' show Thunder;

/// A chip which displays the given user and instance information. Additionally, it renders special chips for special users.
Expand Down Expand Up @@ -60,6 +61,13 @@ class UserChip extends StatelessWidget {
ignoring: ignorePointerEvents,
child: Tooltip(
excludeFromSemantics: true,
triggerMode: TooltipTriggerMode.longPress,
onTriggered: () => copyActivityPubFullName(
type: ActivityPubFullNameType.user,
name: user.name,
displayName: user.displayName,
instance: fetchInstanceNameFromUrl(user.actorId),
),
message: '${generateUserFullName(
context,
user.name,
Expand Down