Skip to content

Commit

Permalink
Fix jank when using full height images, and other misc media preview …
Browse files Browse the repository at this point in the history
…improvements (#1250)

* fix issue where full image height generates jank

* fixed issue where images would be squished or stretched when not in full height image mode

* switched constants to use view mode heights

* added timeout for retrieving image dimensions, misc changes

* remove extra debug prints

* fixed positioning of nsfw warning
  • Loading branch information
hjiangsu authored Mar 31, 2024
1 parent 5fbcbc8 commit f7686f2
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 31 deletions.
10 changes: 9 additions & 1 deletion lib/core/enums/view_mode.dart
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
enum ViewMode { compact, comfortable }
enum ViewMode {
compact(75.0),
comfortable(150.0);

/// The height of media previews for the given view mode
final double height;

const ViewMode(this.height);
}
7 changes: 5 additions & 2 deletions lib/post/utils/post.dart
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ Future<PostViewMedia> parsePostView(PostView postView, bool fetchImageDimensions
if (thumbnailUrl != null && thumbnailUrl.isNotEmpty) {
// Now check to see if there is a thumbnail image. If there is, we'll use that for the image
media.mediaUrl = thumbnailUrl;
} else if (isImage) {
// If there is no thumbnail image, but the url is an image, we'll use that for the mediaUrl
media.mediaUrl = url;
} else if (scrapeMissingPreviews) {
// If there is no thumbnail image, we'll see if we should try to fetch the link metadata
LinkInfo linkInfo = await getLinkInfo(url);
Expand All @@ -313,9 +316,9 @@ Future<PostViewMedia> parsePostView(PostView postView, bool fetchImageDimensions
Size result = Size(MediaQuery.of(GlobalContext.context).size.width, 200);

try {
result = await retrieveImageDimensions(imageUrl: media.mediaUrl);
result = await retrieveImageDimensions(imageUrl: media.mediaUrl ?? media.originalUrl).timeout(const Duration(seconds: 2));
} catch (e) {
debugPrint('$e: Falling back to default image size');
debugPrint('${media.mediaUrl ?? media.originalUrl} - $e: Falling back to default image size');
}

Size size = MediaExtension.getScaledMediaSize(width: result.width, height: result.height, offset: edgeToEdgeImages ? 0 : 24, tabletMode: tabletMode);
Expand Down
9 changes: 5 additions & 4 deletions lib/settings/pages/post_appearance_settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:thunder/core/enums/custom_theme_type.dart';
import 'package:thunder/core/enums/feed_card_divider_thickness.dart';
import 'package:thunder/core/enums/local_settings.dart';
import 'package:thunder/core/enums/post_body_view_type.dart';
import 'package:thunder/core/enums/view_mode.dart';
import 'package:thunder/core/models/post_view_media.dart';
import 'package:thunder/core/singletons/preferences.dart';
import 'package:thunder/feed/feed.dart';
Expand Down Expand Up @@ -1160,8 +1161,8 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
children: [
!showThumbnailPreviewOnRight
? Container(
width: 75,
height: 75,
width: ViewMode.compact.height,
height: ViewMode.compact.height,
margin: const EdgeInsets.only(right: 8.0),
decoration: BoxDecoration(
color: theme.dividerColor,
Expand Down Expand Up @@ -1213,8 +1214,8 @@ class _PostAppearanceSettingsPageState extends State<PostAppearanceSettingsPage>
),
showThumbnailPreviewOnRight
? Container(
width: 75,
height: 75,
width: ViewMode.compact.height,
height: ViewMode.compact.height,
margin: const EdgeInsets.only(right: 8.0),
decoration: BoxDecoration(
color: theme.dividerColor,
Expand Down
22 changes: 11 additions & 11 deletions lib/shared/link_preview_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,21 @@ class LinkPreviewCard extends StatelessWidget {
child: ImagePreview(
read: read,
url: mediaURL ?? originURL!,
height: showFullHeightImages ? mediaHeight : 150,
height: showFullHeightImages ? mediaHeight : ViewMode.comfortable.height,
width: mediaWidth ?? MediaQuery.of(context).size.width - (edgeToEdgeImages ? 0 : 24),
isExpandable: false,
),
)
: ImagePreview(
read: read,
url: mediaURL ?? originURL!,
height: showFullHeightImages ? mediaHeight : 150,
height: showFullHeightImages ? mediaHeight : ViewMode.comfortable.height,
width: mediaWidth ?? MediaQuery.of(context).size.width - (edgeToEdgeImages ? 0 : 24),
isExpandable: false,
)
] else if (scrapeMissingPreviews)
SizedBox(
height: 150,
height: ViewMode.comfortable.height,
// This is used for external links when Lemmy does not provide a preview thumbnail
// and when the user has enabled external scraping.
// This is only used in comfortable mode.
Expand Down Expand Up @@ -161,22 +161,22 @@ class LinkPreviewCard extends StatelessWidget {
child: ImagePreview(
read: read,
url: mediaURL!,
height: 75,
width: 75,
height: ViewMode.compact.height,
width: ViewMode.compact.height,
isExpandable: false,
),
)
: ImagePreview(
read: read,
url: mediaURL!,
height: 75,
width: 75,
height: ViewMode.compact.height,
width: ViewMode.compact.height,
isExpandable: false,
)
: scrapeMissingPreviews
? SizedBox(
height: 75,
width: 75,
height: ViewMode.compact.height,
width: ViewMode.compact.height,
// This is used for external links when Lemmy does not provide a preview thumbnail
// and when the user has enabled external scraping.
// This is only used in compact mode.
Expand All @@ -201,8 +201,8 @@ class LinkPreviewCard extends StatelessWidget {
// This is used for link previews when no thumbnail comes from Lemmy
// and the user has disabled scraping. This is only in compact mode.
: Container(
height: 75,
width: 75,
height: ViewMode.compact.height,
width: ViewMode.compact.height,
color: theme.cardColor.darken(5),
child: Icon(
hideNsfw ? null : Icons.language,
Expand Down
52 changes: 42 additions & 10 deletions lib/shared/media_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ class _MediaViewState extends State<MediaView> with SingleTickerProviderStateMix
color: theme.cardColor.darken(5),
child: widget.postViewMedia.postView.post.body?.isNotEmpty == true
? SizedBox(
height: 75.0,
width: 75.0,
height: ViewMode.compact.height,
width: ViewMode.compact.height,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Align(
Expand All @@ -113,8 +113,8 @@ class _MediaViewState extends State<MediaView> with SingleTickerProviderStateMix
),
)
: Container(
height: 75,
width: 75,
height: ViewMode.compact.height,
width: ViewMode.compact.height,
color: theme.cardColor.darken(5),
child: Icon(
Icons.text_fields_rounded,
Expand Down Expand Up @@ -163,8 +163,29 @@ class _MediaViewState extends State<MediaView> with SingleTickerProviderStateMix
},
child: Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(borderRadius: BorderRadius.circular((widget.edgeToEdgeImages ? 0 : 12))),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular((widget.edgeToEdgeImages ? 0 : 12)),
color: theme.colorScheme.primary.withOpacity(0.2),
),
constraints: BoxConstraints(
maxHeight: switch (widget.viewMode) {
ViewMode.compact => ViewMode.compact.height,
ViewMode.comfortable => widget.showFullHeightImages ? widget.postViewMedia.media.first.height ?? ViewMode.comfortable.height : ViewMode.comfortable.height,
},
minHeight: switch (widget.viewMode) {
ViewMode.compact => ViewMode.compact.height,
ViewMode.comfortable => widget.showFullHeightImages ? widget.postViewMedia.media.first.height ?? ViewMode.comfortable.height : ViewMode.comfortable.height,
},
maxWidth: switch (widget.viewMode) {
ViewMode.compact => ViewMode.compact.height,
ViewMode.comfortable => widget.edgeToEdgeImages ? double.infinity : MediaQuery.of(context).size.width,
},
minWidth: switch (widget.viewMode) {
ViewMode.compact => ViewMode.compact.height,
ViewMode.comfortable => widget.edgeToEdgeImages ? double.infinity : MediaQuery.of(context).size.width,
}),
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
ImageFiltered(
Expand All @@ -174,6 +195,8 @@ class _MediaViewState extends State<MediaView> with SingleTickerProviderStateMix
),
if (blurNSFWPreviews)
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.warning_rounded, size: widget.viewMode != ViewMode.compact ? 55 : 30),
if (widget.viewMode != ViewMode.compact) Text(l10n.nsfwWarning, textScaler: const TextScaler.linear(1.5)),
Expand Down Expand Up @@ -218,8 +241,18 @@ class _MediaViewState extends State<MediaView> with SingleTickerProviderStateMix
final theme = Theme.of(context);
final state = context.read<ThunderBloc>().state;

double? height = widget.viewMode == ViewMode.compact ? 75 : (widget.showFullHeightImages ? widget.postViewMedia.media.first.height : 150);
double width = widget.viewMode == ViewMode.compact ? 75 : MediaQuery.of(context).size.width - (widget.edgeToEdgeImages ? 0 : 24);
double? width;
double? height;

switch (widget.viewMode) {
case ViewMode.compact:
width = null; // Setting this to null will use the image's width. This will allow the image to not be stretched or squished.
height = ViewMode.compact.height;
break;
case ViewMode.comfortable:
width = MediaQuery.of(context).size.width - (widget.edgeToEdgeImages ? 0 : 24);
height = widget.showFullHeightImages ? widget.postViewMedia.media.first.height : null;
}

return ExtendedImage.network(
color: widget.read == true ? const Color.fromRGBO(255, 255, 255, 0.5) : null,
Expand All @@ -230,9 +263,8 @@ class _MediaViewState extends State<MediaView> with SingleTickerProviderStateMix
fit: widget.viewMode == ViewMode.compact ? BoxFit.cover : BoxFit.fitWidth,
cache: true,
clearMemoryCacheWhenDispose: state.imageCachingMode == ImageCachingMode.relaxed,
cacheWidth: widget.viewMode == ViewMode.compact
? (75 * View.of(context).devicePixelRatio.ceil())
: ((MediaQuery.of(context).size.width - (widget.edgeToEdgeImages ? 0 : 24)) * View.of(context).devicePixelRatio.ceil()).toInt(),
cacheWidth: width != null ? (width * View.of(context).devicePixelRatio.ceil()).toInt() : null,
cacheHeight: height != null ? (height * View.of(context).devicePixelRatio.ceil()).toInt() : null,
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
Expand Down
8 changes: 5 additions & 3 deletions lib/shared/preview_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class _PreviewImageState extends State<PreviewImage> with SingleTickerProviderSt
final ThunderState state = context.read<ThunderBloc>().state;
final useDarkTheme = state.themeType == 'dark';

double? height = widget.viewMode == ViewMode.compact ? 75 : (widget.showFullHeightImages ? widget.height : 150);
double width = widget.viewMode == ViewMode.compact ? 75 : MediaQuery.of(context).size.width - 24;
double? height = widget.viewMode == ViewMode.compact ? ViewMode.compact.height : (widget.showFullHeightImages ? widget.height : ViewMode.comfortable.height);
double width = widget.viewMode == ViewMode.compact ? ViewMode.compact.height : MediaQuery.of(context).size.width - 24;

return ExtendedImage.network(
widget.mediaUrl,
Expand All @@ -57,7 +57,9 @@ class _PreviewImageState extends State<PreviewImage> with SingleTickerProviderSt
fit: widget.viewMode == ViewMode.compact ? BoxFit.cover : BoxFit.fitWidth,
cache: true,
clearMemoryCacheWhenDispose: state.imageCachingMode == ImageCachingMode.relaxed,
cacheWidth: widget.viewMode == ViewMode.compact ? (75 * View.of(context).devicePixelRatio.ceil()) : ((MediaQuery.of(context).size.width - 24) * View.of(context).devicePixelRatio.ceil()).toInt(),
cacheWidth: widget.viewMode == ViewMode.compact
? (ViewMode.compact.height * View.of(context).devicePixelRatio.ceil()).toInt()
: ((MediaQuery.of(context).size.width - 24) * View.of(context).devicePixelRatio.ceil()).toInt(),
loadStateChanged: (ExtendedImageState state) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
Expand Down

0 comments on commit f7686f2

Please sign in to comment.