Skip to content

Commit 048f60f

Browse files
authored
Distinguish direct video URLs and refactor of parsePostView/MediaView (#1192)
* refactored parsePostView and MediaView, added video type badge * more refactoring, added comments and localization to strings * fixed null issue
1 parent b92cecc commit 048f60f

17 files changed

+276
-288
lines changed

lib/community/pages/create_post_page.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import 'package:thunder/shared/snackbar.dart';
3636
import 'package:thunder/user/utils/restore_user.dart';
3737
import 'package:thunder/user/widgets/user_selector.dart';
3838
import 'package:thunder/utils/debounce.dart';
39-
import 'package:thunder/utils/image.dart';
39+
import 'package:thunder/utils/media/image.dart';
4040
import 'package:thunder/utils/instance.dart';
4141

4242
class CreatePostPage extends StatefulWidget {

lib/community/widgets/post_card_type_badge.dart

+6-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ class TypeBadge extends StatelessWidget {
4343
MediaType.image: MediaTypeBadgeItem(
4444
baseColor: Colors.red,
4545
icon: Icon(size: 17, Icons.image_outlined, color: getIconColor(theme, Colors.red)),
46-
)
46+
),
47+
MediaType.video: MediaTypeBadgeItem(
48+
baseColor: Colors.purple,
49+
icon: Icon(size: 17, Icons.play_arrow_rounded, color: getIconColor(theme, Colors.purple)),
50+
),
4751
};
4852

4953
return SizedBox(
@@ -60,6 +64,7 @@ class TypeBadge extends StatelessWidget {
6064
MediaType.text => typeBadgeItem(context, mediaTypeItems[MediaType.text]!),
6165
MediaType.link => typeBadgeItem(context, mediaTypeItems[MediaType.link]!),
6266
MediaType.image => typeBadgeItem(context, mediaTypeItems[MediaType.image]!),
67+
MediaType.video => typeBadgeItem(context, mediaTypeItems[MediaType.video]!),
6368
_ => typeBadgeItem(context, mediaTypeItems[MediaType.text]!),
6469
},
6570
),

lib/community/widgets/post_card_view_comfortable.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class PostCardViewComfortable extends StatelessWidget {
8181

8282
var mediaView = MediaView(
8383
scrapeMissingPreviews: state.scrapeMissingPreviews,
84-
postView: postViewMedia,
84+
postViewMedia: postViewMedia,
8585
showFullHeightImages: showFullHeightImages,
8686
hideNsfwPreviews: hideNsfwPreviews,
8787
edgeToEdgeImages: edgeToEdgeImages,

lib/community/widgets/post_card_view_compact.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class ThumbnailPreview extends StatelessWidget {
213213
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 4),
214214
child: MediaView(
215215
scrapeMissingPreviews: state.scrapeMissingPreviews,
216-
postView: postViewMedia,
216+
postViewMedia: postViewMedia,
217217
showFullHeightImages: false,
218218
hideNsfwPreviews: hideNsfwPreviews,
219219
markPostReadOnMediaView: markPostReadOnMediaView,

lib/core/enums/media_type.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
enum MediaType { image, video, gallery, link, text }
1+
enum MediaType { image, video, link, text }

lib/core/models/pictr_media_extension.dart

-36
This file was deleted.

lib/l10n/app_en.arb

+4
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,10 @@
935935
"@notificationsWarningDialog": {
936936
"description": "The content of the warning dialog for the notifications feature"
937937
},
938+
"nsfwWarning": "NSFW - Tap to reveal",
939+
"@nsfwWarning": {
940+
"description": "Warning for NSFW posts"
941+
},
938942
"off": "off",
939943
"@off": {},
940944
"ok": "OK",

lib/post/pages/create_comment_page.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import 'package:thunder/shared/snackbar.dart';
1818
import 'package:thunder/shared/text/scalable_text.dart';
1919
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
2020
import 'package:thunder/user/widgets/user_indicator.dart';
21-
import 'package:thunder/utils/image.dart';
21+
import 'package:thunder/utils/media/image.dart';
2222
import 'package:thunder/utils/instance.dart';
2323

2424
class CreateCommentPage extends StatefulWidget {
@@ -281,7 +281,7 @@ class _CreateCommentPageState extends State<CreateCommentPage> {
281281
),
282282
MediaView(
283283
scrapeMissingPreviews: thunderState.scrapeMissingPreviews,
284-
postView: widget.postView,
284+
postViewMedia: widget.postView!,
285285
hideNsfwPreviews: thunderState.hideNsfwPreviews,
286286
markPostReadOnMediaView: thunderState.markPostReadOnMediaView,
287287
isUserLoggedIn: true,

lib/post/utils/post.dart

+57-80
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import 'package:thunder/core/models/media_extension.dart';
1414
import 'package:thunder/core/models/post_view_media.dart';
1515
import 'package:thunder/core/singletons/lemmy_client.dart';
1616
import 'package:thunder/core/singletons/preferences.dart';
17-
import 'package:thunder/utils/image.dart';
17+
import 'package:thunder/utils/media/image.dart';
1818
import 'package:thunder/utils/links.dart';
19+
import 'package:thunder/utils/media/video.dart';
1920

2021
extension on MarkPostAsReadResponse {
2122
bool isSuccess() {
@@ -237,8 +238,10 @@ Future<List<PostViewMedia>> parsePostViews(List<PostView> postViews, {String? re
237238
bool edgeToEdgeImages = prefs.getBool(LocalSettings.showPostEdgeToEdgeImages.name) ?? false;
238239
bool tabletMode = prefs.getBool(LocalSettings.useTabletMode.name) ?? false;
239240
bool hideNsfwPosts = prefs.getBool(LocalSettings.hideNsfwPosts.name) ?? false;
241+
bool scrapeMissingPreviews = prefs.getBool(LocalSettings.scrapeMissingPreviews.name) ?? false;
240242

241243
List<PostView> postViewsFinal = [];
244+
242245
if (resolutionInstance != null) {
243246
final LemmyApiV3 lemmy = (LemmyClient()..changeBaseUrl(resolutionInstance)).lemmyApiV3;
244247

@@ -254,92 +257,66 @@ Future<List<PostViewMedia>> parsePostViews(List<PostView> postViews, {String? re
254257
postViewsFinal = postViews.toList();
255258
}
256259

257-
Iterable<Future<PostViewMedia>> postFutures =
258-
postViewsFinal.expand((post) => [if (!hideNsfwPosts || (!post.post.nsfw && hideNsfwPosts)) parsePostView(post, fetchImageDimensions, edgeToEdgeImages, tabletMode)]).toList();
259-
List<PostViewMedia> posts = await Future.wait(postFutures);
260+
Iterable<Future<PostViewMedia>> postFutures = postViewsFinal
261+
.expand(
262+
(post) => [
263+
if (!hideNsfwPosts || (!post.post.nsfw && hideNsfwPosts)) parsePostView(post, fetchImageDimensions, edgeToEdgeImages, tabletMode, scrapeMissingPreviews),
264+
],
265+
)
266+
.toList();
260267

268+
List<PostViewMedia> posts = await Future.wait(postFutures);
261269
return posts;
262270
}
263271

264-
Future<PostViewMedia> parsePostView(PostView postView, bool fetchImageDimensions, bool edgeToEdgeImages, bool tabletMode) async {
265-
List<Media> media = [];
266-
String? url = postView.post.url;
272+
Future<PostViewMedia> parsePostView(PostView postView, bool fetchImageDimensions, bool edgeToEdgeImages, bool tabletMode, bool scrapeMissingPreviews) async {
273+
List<Media> mediaList = [];
267274

268-
if (url != null && isImageUrl(url)) {
269-
try {
270-
MediaType mediaType = MediaType.image;
275+
// There are three sources of URLs: the main url attached to the post, the thumbnail url attached to the post, and the video url attached to the post
276+
String? url = postView.post.url ?? '';
277+
String? thumbnailUrl = postView.post.thumbnailUrl;
278+
String? videoUrl = postView.post.embedVideoUrl; // @TODO: Add support for videos
271279

272-
if (fetchImageDimensions) {
273-
Size result = await retrieveImageDimensions(imageUrl: url);
274-
Size size = MediaExtension.getScaledMediaSize(width: result.width, height: result.height, offset: edgeToEdgeImages ? 0 : 24, tabletMode: tabletMode);
275-
media.add(Media(mediaUrl: url, originalUrl: url, width: size.width, height: size.height, mediaType: mediaType));
276-
} else {
277-
media.add(Media(mediaUrl: url, originalUrl: url, mediaType: mediaType));
278-
}
279-
} catch (e) {
280-
// If it fails, fall back to a media type of link
281-
media.add(Media(originalUrl: url, mediaType: MediaType.link));
282-
}
283-
} else if (url != null) {
284-
if (fetchImageDimensions) {
285-
if (postView.post.thumbnailUrl?.isNotEmpty == true) {
286-
try {
287-
Size result = await retrieveImageDimensions(imageUrl: postView.post.thumbnailUrl!);
288-
Size size = MediaExtension.getScaledMediaSize(width: result.width, height: result.height, offset: edgeToEdgeImages ? 0 : 24, tabletMode: tabletMode);
289-
media.add(Media(
290-
mediaUrl: postView.post.thumbnailUrl!,
291-
mediaType: MediaType.link,
292-
originalUrl: url,
293-
width: size.width,
294-
height: size.height,
295-
));
296-
} catch (e) {
297-
// If it fails, fall back to a media type of link
298-
media.add(Media(originalUrl: url, mediaType: MediaType.link));
299-
}
300-
} else {
301-
try {
302-
// For external links, attempt to fetch any media associated with it (image, title)
303-
LinkInfo linkInfo = await getLinkInfo(url);
304-
305-
if (linkInfo.imageURL != null && linkInfo.imageURL!.isNotEmpty) {
306-
Size result = await retrieveImageDimensions(imageUrl: linkInfo.imageURL!);
307-
308-
int mediaHeight = result.height.toInt();
309-
int mediaWidth = result.width.toInt();
310-
Size size = MediaExtension.getScaledMediaSize(width: mediaWidth, height: mediaHeight, offset: edgeToEdgeImages ? 0 : 24, tabletMode: tabletMode);
311-
media.add(Media(mediaUrl: linkInfo.imageURL!, mediaType: MediaType.link, originalUrl: url, height: size.height, width: size.width));
312-
} else {
313-
media.add(Media(mediaUrl: linkInfo.imageURL!, mediaType: MediaType.link, originalUrl: url));
314-
}
315-
} catch (e) {
316-
// Default back to a link
317-
media.add(Media(mediaType: MediaType.link, originalUrl: url));
318-
}
319-
}
320-
} else {
321-
if (postView.post.thumbnailUrl?.isNotEmpty == true) {
322-
media.add(Media(mediaUrl: postView.post.thumbnailUrl!, mediaType: MediaType.link, originalUrl: url));
323-
} else {
324-
media.add(Media(mediaType: MediaType.link, originalUrl: url));
325-
}
280+
// First, check what type of link we're dealing with based on the url (MediaType.image, MediaType.video, MediaType.link, MediaType.text)
281+
bool isImage = isImageUrl(url);
282+
bool isVideo = isVideoUrl(url);
283+
284+
MediaType mediaType;
285+
286+
if (isImage) {
287+
mediaType = MediaType.image;
288+
} else if (isVideo) {
289+
mediaType = MediaType.video;
290+
} else if (url.isNotEmpty) {
291+
mediaType = MediaType.link;
292+
} else {
293+
mediaType = MediaType.text;
294+
}
295+
296+
Media media = Media(mediaType: mediaType, originalUrl: url);
297+
298+
if (thumbnailUrl != null && thumbnailUrl.isNotEmpty) {
299+
// Now check to see if there is a thumbnail image. If there is, we'll use that for the image
300+
media.mediaUrl = thumbnailUrl;
301+
} else if (scrapeMissingPreviews) {
302+
// If there is no thumbnail image, we'll see if we should try to fetch the link metadata
303+
LinkInfo linkInfo = await getLinkInfo(url);
304+
305+
if (linkInfo.imageURL != null && linkInfo.imageURL!.isNotEmpty) {
306+
media.mediaUrl = linkInfo.imageURL!;
326307
}
327308
}
328309

329-
return PostViewMedia(
330-
postView: PostView(
331-
community: postView.community,
332-
counts: postView.counts,
333-
creator: postView.creator,
334-
creatorBannedFromCommunity: postView.creatorBannedFromCommunity,
335-
creatorBlocked: postView.creatorBlocked,
336-
myVote: postView.myVote,
337-
post: postView.post,
338-
read: postView.read,
339-
saved: postView.saved,
340-
subscribed: postView.subscribed,
341-
unreadComments: postView.unreadComments,
342-
),
343-
media: media,
344-
);
310+
// Finally, check to see if we need to fetch the image dimensions
311+
if (fetchImageDimensions && media.mediaUrl != null) {
312+
Size result = await retrieveImageDimensions(imageUrl: media.mediaUrl);
313+
Size size = MediaExtension.getScaledMediaSize(width: result.width, height: result.height, offset: edgeToEdgeImages ? 0 : 24, tabletMode: tabletMode);
314+
315+
media.width = size.width;
316+
media.height = size.height;
317+
}
318+
319+
mediaList.add(media);
320+
321+
return PostViewMedia(postView: postView, media: mediaList);
345322
}

lib/post/widgets/post_view.dart

+2-3
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,7 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
148148
collapsed: Container(),
149149
expanded: MediaView(
150150
scrapeMissingPreviews: scrapeMissingPreviews,
151-
post: post,
152-
postView: widget.postViewMedia,
151+
postViewMedia: widget.postViewMedia,
153152
hideNsfwPreviews: hideNsfwPreviews,
154153
markPostReadOnMediaView: markPostReadOnMediaView,
155154
isUserLoggedIn: isUserLoggedIn,
@@ -542,7 +541,7 @@ class _PostSubviewState extends State<PostSubview> with SingleTickerProviderStat
542541
),
543542
child: MediaView(
544543
scrapeMissingPreviews: thunderState.scrapeMissingPreviews,
545-
postView: postViewMedia,
544+
postViewMedia: postViewMedia,
546545
showFullHeightImages: false,
547546
hideNsfwPreviews: hideNsfwPreviews,
548547
markPostReadOnMediaView: markPostReadOnMediaView,

lib/shared/common_markdown_body.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
88
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
99
import 'package:thunder/shared/text/scalable_text.dart';
1010

11-
import 'package:thunder/utils/image.dart';
11+
import 'package:thunder/utils/media/image.dart';
1212
import 'package:thunder/utils/links.dart';
1313
import 'package:thunder/shared/image_preview.dart';
1414
import 'package:thunder/core/enums/font_scale.dart';

lib/shared/image_preview.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
99
import 'package:thunder/core/enums/image_caching_mode.dart';
1010
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
1111

12-
import 'package:thunder/utils/image.dart';
12+
import 'package:thunder/utils/media/image.dart';
1313

1414
class ImagePreview extends StatefulWidget {
1515
final String? url;

lib/shared/image_viewer.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import 'package:thunder/shared/dialogs.dart';
2121
import 'package:thunder/shared/snackbar.dart';
2222
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
2323
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
24-
import 'package:thunder/utils/image.dart';
24+
import 'package:thunder/utils/media/image.dart';
2525

2626
class ImageViewer extends StatefulWidget {
2727
final String? url;

0 commit comments

Comments
 (0)