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 link sharing experience #1204

Merged
merged 1 commit into from
Mar 14, 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
57 changes: 42 additions & 15 deletions lib/community/utils/post_card_action_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import 'package:thunder/post/widgets/reason_bottom_sheet.dart';
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/user/enums/user_action.dart';
import 'package:thunder/utils/instance.dart';
Expand All @@ -49,8 +48,10 @@ enum PostCardAction {
visitInstance,
blockInstance,
sharePost,
sharePostLocal,
shareMedia,
shareLink,
shareAdvanced,
upvote,
downvote,
save,
Expand Down Expand Up @@ -86,7 +87,7 @@ class ExtendedPostCardActions {
final Color? Function(PostView postView)? getForegroundColor;
final IconData? Function(PostView postView)? getOverrideIcon;
final String? Function(BuildContext context, PostView postView)? getOverrideLabel;
final String? Function(BuildContext context, PostView postView)? getSubtitleLabel;
final String? Function(BuildContext context, PostViewMedia postViewMedia)? getSubtitleLabel;
final bool Function(BuildContext context, PostView commentView)? shouldShow;
final bool Function(bool isUserLoggedIn)? shouldEnable;
}
Expand All @@ -98,7 +99,7 @@ final List<ExtendedPostCardActions> postCardActionItems = [
postCardAction: PostCardAction.userActions,
icon: Icons.person_rounded,
label: l10n.user,
getSubtitleLabel: (context, postView) => generateUserFullName(context, postView.creator.name, fetchInstanceNameFromUrl(postView.creator.actorId)),
getSubtitleLabel: (context, postViewMedia) => generateUserFullName(context, postViewMedia.postView.creator.name, fetchInstanceNameFromUrl(postViewMedia.postView.creator.actorId)),
trailingIcon: Icons.chevron_right_rounded,
),
ExtendedPostCardActions(
Expand All @@ -116,7 +117,7 @@ final List<ExtendedPostCardActions> postCardActionItems = [
postCardAction: PostCardAction.communityActions,
icon: Icons.people_rounded,
label: l10n.community,
getSubtitleLabel: (context, postView) => generateCommunityFullName(context, postView.community.name, fetchInstanceNameFromUrl(postView.community.actorId)),
getSubtitleLabel: (context, postViewMedia) => generateCommunityFullName(context, postViewMedia.postView.community.name, fetchInstanceNameFromUrl(postViewMedia.postView.community.actorId)),
trailingIcon: Icons.chevron_right_rounded,
),
ExtendedPostCardActions(
Expand Down Expand Up @@ -146,7 +147,7 @@ final List<ExtendedPostCardActions> postCardActionItems = [
postCardAction: PostCardAction.instanceActions,
icon: Icons.language_rounded,
label: l10n.instance(1),
getSubtitleLabel: (context, postView) => fetchInstanceNameFromUrl(postView.community.actorId) ?? '',
getSubtitleLabel: (context, postViewMedia) => fetchInstanceNameFromUrl(postViewMedia.postView.community.actorId) ?? '',
trailingIcon: Icons.chevron_right_rounded,
),
ExtendedPostCardActions(
Expand All @@ -164,16 +165,31 @@ final List<ExtendedPostCardActions> postCardActionItems = [
postCardAction: PostCardAction.sharePost,
icon: Icons.share_rounded,
label: l10n.sharePost,
getSubtitleLabel: (context, postViewMedia) => postViewMedia.postView.post.apId,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.sharePostLocal,
icon: Icons.share_rounded,
label: l10n.sharePostLocal,
getSubtitleLabel: (context, postViewMedia) => LemmyClient.instance.generatePostUrl(postViewMedia.postView.post.id),
),
ExtendedPostCardActions(
postCardAction: PostCardAction.shareMedia,
icon: Icons.image_rounded,
label: l10n.shareMedia,
getSubtitleLabel: (context, postViewMedia) => postViewMedia.media.first.mediaUrl,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.shareLink,
icon: Icons.link_rounded,
label: l10n.shareLink,
getSubtitleLabel: (context, postViewMedia) => postViewMedia.media.first.originalUrl,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.shareAdvanced,
icon: Icons.screen_share_rounded,
label: l10n.advanced,
getSubtitleLabel: (context, postViewMedia) => l10n.useAdvancedShareSheet,
),
ExtendedPostCardActions(
postCardAction: PostCardAction.upvote,
Expand Down Expand Up @@ -262,7 +278,7 @@ enum PostActionBottomSheetPage {
void showPostActionBottomModalSheet(
BuildContext context,
PostViewMedia postViewMedia, {
PostActionBottomSheetPage page = PostActionBottomSheetPage.general, // todo: is this needed?
PostActionBottomSheetPage page = PostActionBottomSheetPage.general,
}) {
final bool isOwnPost = postViewMedia.postView.creator.id == context.read<AuthBloc>().state.account?.userId;

Expand Down Expand Up @@ -308,13 +324,18 @@ void showPostActionBottomModalSheet(
final List<ExtendedPostCardActions> sharePostCardActions = postCardActionItems
.where((extendedAction) => [
PostCardAction.sharePost,
PostCardAction.sharePostLocal,
PostCardAction.shareMedia,
PostCardAction.shareLink,
PostCardAction.shareAdvanced,
].contains(extendedAction.postCardAction))
.toList();

// Remove the share link option if there is no link
if (postViewMedia.media.isEmpty || (postViewMedia.media.first.mediaType != MediaType.link && postViewMedia.media.first.mediaType != MediaType.image)) {
// Or if the media link is the same as the external link
if (postViewMedia.media.isEmpty ||
(postViewMedia.media.first.mediaType != MediaType.link && postViewMedia.media.first.mediaType != MediaType.image) ||
postViewMedia.media.first.originalUrl == postViewMedia.media.first.mediaUrl) {
sharePostCardActions.removeWhere((extendedAction) => extendedAction.postCardAction == PostCardAction.shareLink);
}

Expand All @@ -323,6 +344,11 @@ void showPostActionBottomModalSheet(
sharePostCardActions.removeWhere((extendedAction) => extendedAction.postCardAction == PostCardAction.shareMedia);
}

// Remove the share local option if it is the same as the original
if (postViewMedia.postView.post.apId == LemmyClient.instance.generatePostUrl(postViewMedia.postView.post.id)) {
sharePostCardActions.removeWhere((extendedAction) => extendedAction.postCardAction == PostCardAction.sharePostLocal);
}

// Generate the list of user actions
final List<ExtendedPostCardActions> userActions = postCardActionItems
.where((extendedAction) => [
Expand Down Expand Up @@ -511,7 +537,7 @@ class _PostCardActionPickerState extends State<PostCardActionPicker> {
return PickerItem(
label: widget.postCardActions[page ?? widget.page]![index].getOverrideLabel?.call(context, widget.postViewMedia.postView) ??
widget.postCardActions[page ?? widget.page]![index].label,
subtitle: widget.postCardActions[page ?? widget.page]![index].getSubtitleLabel?.call(context, widget.postViewMedia.postView),
subtitle: widget.postCardActions[page ?? widget.page]![index].getSubtitleLabel?.call(context, widget.postViewMedia),
icon: widget.postCardActions[page ?? widget.page]![index].getOverrideIcon?.call(widget.postViewMedia.postView) ?? widget.postCardActions[page ?? widget.page]![index].icon,
trailingIcon: widget.postCardActions[page ?? widget.page]![index].trailingIcon,
onSelected: (widget.postCardActions[page ?? widget.page]![index].shouldEnable?.call(isUserLoggedIn) ?? true)
Expand Down Expand Up @@ -550,6 +576,9 @@ class _PostCardActionPickerState extends State<PostCardActionPicker> {
case PostCardAction.sharePost:
action = () => Share.share(widget.postViewMedia.postView.post.apId);
break;
case PostCardAction.sharePostLocal:
action = () => Share.share(LemmyClient.instance.generatePostUrl(widget.postViewMedia.postView.post.id));
break;
case PostCardAction.shareMedia:
action = () async {
if (widget.postViewMedia.media.first.mediaUrl != null) {
Expand Down Expand Up @@ -580,6 +609,9 @@ class _PostCardActionPickerState extends State<PostCardActionPicker> {
if (widget.postViewMedia.media.first.originalUrl != null) Share.share(widget.postViewMedia.media.first.originalUrl!);
};
break;
case PostCardAction.shareAdvanced:
action = () => showAdvancedShareSheet(widget.outerContext, widget.postViewMedia);
break;
case PostCardAction.instanceActions:
action = () => setState(() => page = PostActionBottomSheetPage.instance);
pop = false;
Expand Down Expand Up @@ -619,13 +651,8 @@ class _PostCardActionPickerState extends State<PostCardActionPicker> {
widget.outerContext.read<FeedBloc>().add(FeedItemActionedEvent(postAction: PostAction.read, postId: widget.postViewMedia.postView.post.id, value: !widget.postViewMedia.postView.read));
break;
case PostCardAction.share:
final bool useAdvancedShareSheet = widget.outerContext.read<ThunderBloc>().state.useAdvancedShareSheet;
if (useAdvancedShareSheet) {
action = () => showAdvancedShareSheet(widget.outerContext, widget.postViewMedia);
} else {
pop = false;
action = () => setState(() => page = PostActionBottomSheetPage.share);
}
pop = false;
action = () => setState(() => page = PostActionBottomSheetPage.share);
break;
case PostCardAction.blockUser:
action = () => widget.outerContext.read<UserBloc>().add(UserActionEvent(userAction: UserAction.block, userId: widget.postViewMedia.postView.creator.id, value: true));
Expand Down
3 changes: 1 addition & 2 deletions lib/core/enums/local_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ enum LocalSettings {
showPostTextContentPreview(name: 'setting_general_show_text_content', key: 'showPostTextContentPreview', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.cardView),
showPostAuthor(name: 'setting_general_show_post_author', key: 'showPostAuthor', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.general),
dimReadPosts(name: 'setting_dim_read_posts', key: 'dimReadPosts', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.general),
useAdvancedShareSheet(name: 'setting_use_advanced_share_sheet', key: 'useAdvancedShareSheet', category: LocalSettingsCategories.general, subCategory: LocalSettingsSubCategories.posts),
showCrossPosts(name: 'setting_show_cross_posts', key: 'showCrossPosts', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.general),
keywordFilters(name: 'setting_general_keyword_filters', key: 'keywordFilters', category: LocalSettingsCategories.filters, subCategory: LocalSettingsSubCategories.filters),
hideTopBarOnScroll(name: 'setting_general_hide_topbar_on_scroll', key: 'hideTopBarOnScroll', category: LocalSettingsCategories.general, subCategory: LocalSettingsSubCategories.feed),
Expand Down Expand Up @@ -220,6 +219,7 @@ enum LocalSettings {
anonymousInstances(name: 'setting_anonymous_instances', key: ''),
currentAnonymousInstance(name: 'setting_current_anonymous_instance', key: ''),

// This setting exists purely to save/load the user's selected advanced share options
advancedShareOptions(name: 'advanced_share_options', key: ''),
// import export settings
importExportSettings(name: 'import_export_settings', key: 'importExportSettings', category: LocalSettingsCategories.general, subCategory: LocalSettingsSubCategories.importExportSettings);
Expand Down Expand Up @@ -294,7 +294,6 @@ extension LocalizationExt on AppLocalizations {
'dimReadPosts': dimReadPosts,
'showFullPostDate': showFullDate,
'dateFormat': dateFormat,
'useAdvancedShareSheet': useAdvancedShareSheet,
'showCrossPosts': showCrossPosts,
'keywordFilters': keywordFilters,
'hideTopBarOnScroll': hideTopBarOnScroll,
Expand Down
8 changes: 8 additions & 0 deletions lib/core/singletons/lemmy_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ class LemmyClient {
return instanceVersion > feature.minSupportedVersion;
}

String generatePostUrl(int id) => 'https://${lemmyApiV3.host}/post/$id';

String generateCommentUrl(int id) => 'https://${lemmyApiV3.host}/comment/$id';

String generateCommunityUrl(String community) => 'https://${lemmyApiV3.host}/c/$community';

String generateUserUrl(String community) => 'https://${lemmyApiV3.host}/u/$community';

static final Map<String, GetSiteResponse> _lemmySites = <String, GetSiteResponse>{};
}

Expand Down
64 changes: 64 additions & 0 deletions lib/feed/utils/community_share.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:flutter/material.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/utils/bottom_sheet_list_picker.dart';
import 'package:thunder/utils/instance.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

enum CommunityShareOptions {
link,
localLink,
lemmy,
}

/// Shows a mottom modal sheet which allows sharing the given [communityView].
Future<void> showCommunityShareSheet(BuildContext context, CommunityView communityView) async {
final AppLocalizations l10n = AppLocalizations.of(context)!;

String community = await getLemmyCommunity(communityView.community.actorId) ?? '';
String lemmyLink = '!$community';
String localLink = LemmyClient.instance.generateCommunityUrl(community);

if (context.mounted) {
showModalBottomSheet(
showDragHandle: true,
isScrollControlled: true,
context: context,
builder: (builderContext) => BottomSheetListPicker(
title: l10n.shareCommunity,
items: [
ListPickerItem(
label: l10n.shareCommunityLink,
payload: CommunityShareOptions.link,
subtitle: communityView.community.actorId,
icon: Icons.link_rounded,
),
if (!communityView.community.actorId.contains(LemmyClient.instance.lemmyApiV3.host))
ListPickerItem(
label: l10n.shareCommunityLinkLocal,
payload: CommunityShareOptions.localLink,
subtitle: localLink,
icon: Icons.link_rounded,
),
ListPickerItem(
label: l10n.shareLemmyLink,
payload: CommunityShareOptions.lemmy,
subtitle: lemmyLink,
icon: Icons.share_rounded,
),
],
onSelect: (selection) {
switch (selection.payload) {
case CommunityShareOptions.link:
Share.share(communityView.community.actorId);
case CommunityShareOptions.localLink:
Share.share(localLink);
case CommunityShareOptions.lemmy:
Share.share(lemmyLink);
}
},
),
);
}
}
64 changes: 64 additions & 0 deletions lib/feed/utils/user_share.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:flutter/material.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/utils/bottom_sheet_list_picker.dart';
import 'package:thunder/utils/instance.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

enum UserShareOptions {
link,
localLink,
lemmy,
}

/// Shows a mottom modal sheet which allows sharing the given [personView].
Future<void> showUserShareSheet(BuildContext context, PersonView personView) async {
final AppLocalizations l10n = AppLocalizations.of(context)!;

String user = await getLemmyUser(personView.person.actorId) ?? '';
String lemmyLink = '@$user';
String localLink = LemmyClient.instance.generateUserUrl(user);

if (context.mounted) {
showModalBottomSheet(
showDragHandle: true,
isScrollControlled: true,
context: context,
builder: (builderContext) => BottomSheetListPicker(
title: l10n.shareUser,
items: [
ListPickerItem(
label: l10n.shareUserLink,
payload: UserShareOptions.link,
subtitle: personView.person.actorId,
icon: Icons.link_rounded,
),
if (!personView.person.actorId.contains(LemmyClient.instance.lemmyApiV3.host))
ListPickerItem(
label: l10n.shareUserLinkLocal,
payload: UserShareOptions.localLink,
subtitle: localLink,
icon: Icons.link_rounded,
),
ListPickerItem(
label: l10n.shareLemmyLink,
payload: UserShareOptions.lemmy,
subtitle: lemmyLink,
icon: Icons.share_rounded,
),
],
onSelect: (selection) {
switch (selection.payload) {
case UserShareOptions.link:
Share.share(personView.person.actorId);
case UserShareOptions.localLink:
Share.share(localLink);
case UserShareOptions.lemmy:
Share.share(lemmyLink);
}
},
),
);
}
}
7 changes: 4 additions & 3 deletions lib/feed/widgets/feed_page_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:lemmy_api_client/v3.dart';
import 'package:share_plus/share_plus.dart';
import 'package:swipeable_page_route/swipeable_page_route.dart';

import 'package:thunder/account/bloc/account_bloc.dart';
Expand All @@ -16,6 +15,8 @@ import 'package:thunder/community/enums/community_action.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/feed/bloc/feed_bloc.dart';
import 'package:thunder/feed/utils/community.dart';
import 'package:thunder/feed/utils/community_share.dart';
import 'package:thunder/feed/utils/user_share.dart';
import 'package:thunder/feed/utils/utils.dart';
import 'package:thunder/feed/view/feed_page.dart';
import 'package:thunder/modlog/view/modlog_page.dart';
Expand Down Expand Up @@ -190,7 +191,7 @@ class FeedAppBarCommunityActions extends StatelessWidget {
),
if (feedBloc.state.fullCommunityView?.communityView.community.actorId != null)
ThunderPopupMenuItem(
onTap: () => Share.share(feedBloc.state.fullCommunityView!.communityView.community.actorId),
onTap: () => showCommunityShareSheet(context, feedBloc.state.fullCommunityView!.communityView),
icon: Icons.share_rounded,
title: l10n.share,
),
Expand Down Expand Up @@ -287,7 +288,7 @@ class FeedAppBarUserActions extends StatelessWidget {
),
if (feedBloc.state.fullPersonView?.personView.person.actorId != null)
ThunderPopupMenuItem(
onTap: () => Share.share(feedBloc.state.fullPersonView!.personView.person.actorId),
onTap: () => showUserShareSheet(context, feedBloc.state.fullPersonView!.personView),
icon: Icons.share_rounded,
title: l10n.share,
),
Expand Down
Loading
Loading