From 86ea1aa706bd15a8601f0809066723673a276b1a Mon Sep 17 00:00:00 2001 From: --global Date: Wed, 8 May 2024 09:57:41 +0700 Subject: [PATCH] TW-1650: reorganize image_builder folder --- lib/main.dart | 1 + lib/pages/chat/events/event_video_player.dart | 2 +- lib/pages/chat/events/html_message.dart | 2 +- .../{ => images_builder}/image_bubble.dart | 58 ++---------- .../image_builder_web.dart | 78 +--------------- .../images_builder/image_placeholder.dart | 54 +++++++++++ .../message_content_image_builder.dart | 91 +++++++++++++++++++ .../sending_image_info_widget.dart | 44 ++++++--- .../unencrypted_image_builder_web.dart | 72 +++++++++++++++ lib/pages/chat/events/message_content.dart | 79 +--------------- .../chat/events/message_content_style.dart | 1 + lib/pages/chat/events/sticker.dart | 2 +- .../media_page_view_widget.dart | 3 + lib/pages/chat/sticker_picker_dialog.dart | 2 +- lib/pages/image_viewer/image_viewer.dart | 6 ++ lib/pages/image_viewer/image_viewer_view.dart | 24 ++++- .../extension/build_context_extension.dart | 14 ++- lib/widgets/avatar/avatar.dart | 2 +- lib/widgets/mxc_image.dart | 33 ++++--- 19 files changed, 327 insertions(+), 241 deletions(-) rename lib/pages/chat/events/{ => images_builder}/image_bubble.dart (74%) rename lib/pages/chat/events/{ => images_builder}/image_builder_web.dart (50%) create mode 100644 lib/pages/chat/events/images_builder/image_placeholder.dart create mode 100644 lib/pages/chat/events/images_builder/message_content_image_builder.dart rename lib/pages/chat/events/{ => images_builder}/sending_image_info_widget.dart (69%) create mode 100644 lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart diff --git a/lib/main.dart b/lib/main.dart index 835cab8e27..ba2e3811ce 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -38,6 +38,7 @@ void main() async { firstClient?.isSupportDeleteCollections = !PlatformInfos.isWeb; await firstClient?.roomsLoading; await firstClient?.accountDataLoading; + debugInvertOversizedImages = true; // If the app starts in detached mode, we assume that it is in // background fetch mode for processing push notifications. This is diff --git a/lib/pages/chat/events/event_video_player.dart b/lib/pages/chat/events/event_video_player.dart index f87faf3dde..05a115fefe 100644 --- a/lib/pages/chat/events/event_video_player.dart +++ b/lib/pages/chat/events/event_video_player.dart @@ -9,7 +9,7 @@ import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:linagora_design_flutter/linagora_design_flutter.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/pages/chat/events/image_bubble.dart'; +import 'package:fluffychat/pages/chat/events/images_builder/image_bubble.dart'; import 'package:linagora_design_flutter/extensions/duration_extension.dart'; typedef DownloadVideoEventCallback = Future Function(Event event); diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index d83b98b89d..d72ff4120f 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -84,7 +84,7 @@ class HtmlMessage extends StatelessWidget { double? height, { bool? animated = false, }) { - final ratio = MediaQuery.of(context).devicePixelRatio; + final ratio = MediaQuery.devicePixelRatioOf(context); return Uri.parse(mxc) .getThumbnail( matrix.client, diff --git a/lib/pages/chat/events/image_bubble.dart b/lib/pages/chat/events/images_builder/image_bubble.dart similarity index 74% rename from lib/pages/chat/events/image_bubble.dart rename to lib/pages/chat/events/images_builder/image_bubble.dart index fb1e21b9e3..d3b34a31ce 100644 --- a/lib/pages/chat/events/image_bubble.dart +++ b/lib/pages/chat/events/images_builder/image_bubble.dart @@ -1,6 +1,8 @@ -import 'package:fluffychat/pages/chat/events/image_builder_web.dart'; +import 'dart:typed_data'; + +import 'package:fluffychat/pages/chat/events/images_builder/image_builder_web.dart'; +import 'package:fluffychat/pages/chat/events/images_builder/image_placeholder.dart'; import 'package:fluffychat/pages/chat/events/message_content_style.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter/material.dart'; @@ -21,6 +23,7 @@ class ImageBubble extends StatelessWidget { final void Function()? onTapSelectMode; final bool isPreview; final Duration animationDuration; + final Uint8List? imageData; final String? thumbnailCacheKey; final Map? thumbnailCacheMap; @@ -42,6 +45,7 @@ class ImageBubble extends StatelessWidget { this.thumbnailCacheMap, this.noResizeThumbnail = false, this.isPreview = true, + this.imageData, Key? key, }) : super(key: key); @@ -108,6 +112,7 @@ class ImageBubble extends StatelessWidget { cacheKey: thumbnailCacheKey, cacheMap: thumbnailCacheMap, noResize: noResizeThumbnail, + imageData: imageData, ), ], ), @@ -115,52 +120,3 @@ class ImageBubble extends StatelessWidget { ); } } - -class ImagePlaceholder extends StatelessWidget { - const ImagePlaceholder({ - super.key, - required this.event, - required this.width, - required this.height, - required this.fit, - }); - - final Event event; - final double width; - final double height; - final BoxFit fit; - - @override - Widget build(BuildContext context) { - if (event.messageType == MessageTypes.Sticker) { - return const Center( - child: CircularProgressIndicator.adaptive(), - ); - } - final String blurHashString = - event.blurHash ?? AppConfig.defaultImageBlurHash; - final ratio = event.infoMap['w'] is int && event.infoMap['h'] is int - ? event.infoMap['w'] / event.infoMap['h'] - : 1.0; - var width = 32; - var height = 32; - if (ratio > 1.0) { - height = (width / ratio).round(); - } else { - width = (height * ratio).round(); - } - return ClipRRect( - borderRadius: BorderRadius.zero, - child: SizedBox( - width: this.width, - height: this.height, - child: BlurHash( - hash: blurHashString, - decodingWidth: width, - decodingHeight: height, - imageFit: fit, - ), - ), - ); - } -} diff --git a/lib/pages/chat/events/image_builder_web.dart b/lib/pages/chat/events/images_builder/image_builder_web.dart similarity index 50% rename from lib/pages/chat/events/image_builder_web.dart rename to lib/pages/chat/events/images_builder/image_builder_web.dart index 882ac0058a..205667fc32 100644 --- a/lib/pages/chat/events/image_builder_web.dart +++ b/lib/pages/chat/events/images_builder/image_builder_web.dart @@ -1,13 +1,11 @@ -import 'package:fluffychat/pages/chat/events/image_bubble.dart'; +import 'package:fluffychat/pages/chat/events/images_builder/unencrypted_image_builder_web.dart'; import 'package:fluffychat/pages/chat/events/message_content_style.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/presentation/enum/chat/media_viewer_popup_result_enum.dart'; import 'package:fluffychat/utils/interactive_viewer_gallery.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/hero_page_route.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:matrix/matrix.dart'; class UnencryptedImageBuilderWeb extends StatelessWidget { @@ -31,8 +29,8 @@ class UnencryptedImageBuilderWeb extends StatelessWidget { super.key, required this.event, this.isThumbnail = true, - this.width = 256, - this.height = 300, + this.width = MessageContentStyle.imageBubbleWidthForMobileAndTablet, + this.height = MessageContentStyle.imageBubbleHeightForMobileAndTable, this.fit = BoxFit.cover, this.onTapSelectMode, this.onTapPreview, @@ -46,7 +44,7 @@ class UnencryptedImageBuilderWeb extends StatelessWidget { child: Material( child: InkWell( mouseCursor: SystemMouseCursors.click, - borderRadius: BorderRadius.circular(12.0), + borderRadius: MessageContentStyle.borderRadiusBubble, onTap: onTapPreview != null || onTapSelectMode != null ? () => _onTap(context) : null, @@ -82,74 +80,6 @@ class UnencryptedImageBuilderWeb extends StatelessWidget { } } else if (onTapSelectMode != null) { onTapSelectMode!(); - return; - } else { - return; } } } - -class UnencryptedImageWidget extends StatelessWidget { - const UnencryptedImageWidget({ - super.key, - required this.event, - required this.isThumbnail, - required this.width, - required this.height, - required this.fit, - }); - - final Event event; - final bool isThumbnail; - final double width; - final double height; - final BoxFit fit; - - @override - Widget build(BuildContext context) { - return Image.network( - event - .attachmentOrThumbnailMxcUrl(getThumbnail: isThumbnail)! - .getDownloadLink(event.room.client) - .toString(), - frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { - if (wasSynchronouslyLoaded) { - return child; - } - return AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - child: frame != null - ? child - : ImagePlaceholder( - event: event, - width: width, - height: height, - fit: fit, - ), - ); - }, - fit: fit, - width: width, - height: height, - cacheWidth: (width * MediaQuery.of(context).devicePixelRatio).toInt(), - filterQuality: FilterQuality.none, - errorBuilder: (context, error, stackTrace) { - return BlurHash( - hash: event.blurHash ?? MessageContentStyle.defaultBlurHash, - ); - }, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) { - return child; - } - return SizedBox( - width: width, - height: height, - child: BlurHash( - hash: event.blurHash ?? MessageContentStyle.defaultBlurHash, - ), - ); - }, - ); - } -} diff --git a/lib/pages/chat/events/images_builder/image_placeholder.dart b/lib/pages/chat/events/images_builder/image_placeholder.dart new file mode 100644 index 0000000000..a498d1ebb0 --- /dev/null +++ b/lib/pages/chat/events/images_builder/image_placeholder.dart @@ -0,0 +1,54 @@ +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blurhash/flutter_blurhash.dart'; +import 'package:matrix/matrix.dart'; + +class ImagePlaceholder extends StatelessWidget { + const ImagePlaceholder({ + super.key, + required this.event, + required this.width, + required this.height, + required this.fit, + }); + + final Event event; + final double width; + final double height; + final BoxFit fit; + + @override + Widget build(BuildContext context) { + if (event.messageType == MessageTypes.Sticker) { + return const Center( + child: CircularProgressIndicator.adaptive(), + ); + } + final String blurHashString = + event.blurHash ?? AppConfig.defaultImageBlurHash; + final ratio = event.infoMap['w'] is int && event.infoMap['h'] is int + ? event.infoMap['w'] / event.infoMap['h'] + : 1.0; + var width = 32; + var height = 32; + if (ratio > 1.0) { + height = (width / ratio).round(); + } else { + width = (height * ratio).round(); + } + return ClipRRect( + borderRadius: BorderRadius.zero, + child: SizedBox( + width: this.width, + height: this.height, + child: BlurHash( + hash: blurHashString, + decodingWidth: width, + decodingHeight: height, + imageFit: fit, + ), + ), + ); + } +} diff --git a/lib/pages/chat/events/images_builder/message_content_image_builder.dart b/lib/pages/chat/events/images_builder/message_content_image_builder.dart new file mode 100644 index 0000000000..1599d3941b --- /dev/null +++ b/lib/pages/chat/events/images_builder/message_content_image_builder.dart @@ -0,0 +1,91 @@ +import 'package:fluffychat/pages/chat/events/images_builder/image_bubble.dart'; +import 'package:fluffychat/pages/chat/events/message_content_style.dart'; +import 'package:fluffychat/pages/chat/events/images_builder/sending_image_info_widget.dart'; +import 'package:fluffychat/presentation/model/file/display_image_info.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; +import 'package:fluffychat/utils/extension/image_size_extension.dart'; + +class MessageImageBuilder extends StatelessWidget { + final Event event; + + final void Function()? onTapPreview; + + final void Function()? onTapSelectMode; + + const MessageImageBuilder({ + super.key, + required this.event, + this.onTapPreview, + this.onTapSelectMode, + }); + + @override + Widget build(BuildContext context) { + final matrixFile = event.getMatrixFile(); + + DisplayImageInfo? displayImageInfo = + event.getOriginalResolution()?.getDisplayImageInfo(context); + + if (isSendingImageInMobile(matrixFile)) { + final file = matrixFile as MatrixImageFile; + displayImageInfo = Size( + file.width?.toDouble() ?? MessageContentStyle.imageWidth(context), + file.height?.toDouble() ?? MessageContentStyle.imageHeight(context), + ).getDisplayImageInfo(context); + return SendingImageInfoWidget( + key: ValueKey(event.eventId), + matrixFile: file, + event: event, + onTapPreview: onTapPreview, + displayImageInfo: displayImageInfo, + ); + } + displayImageInfo ??= DisplayImageInfo( + size: Size( + MessageContentStyle.imageWidth(context), + MessageContentStyle.imageHeight(context), + ), + hasBlur: true, + ); + if (isSendingImageInWeb(matrixFile)) { + final file = matrixFile as MatrixImageFile; + displayImageInfo = Size( + file.width?.toDouble() ?? MessageContentStyle.imageWidth(context), + file.height?.toDouble() ?? MessageContentStyle.imageHeight(context), + ).getDisplayImageInfo(context); + return SendingImageInfoWidget( + key: ValueKey(event.eventId), + matrixFile: file, + event: event, + onTapPreview: onTapPreview, + displayImageInfo: displayImageInfo, + ); + } + return ImageBubble( + event, + width: displayImageInfo.size.width, + height: displayImageInfo.size.height, + fit: BoxFit.cover, + onTapSelectMode: onTapSelectMode, + onTapPreview: onTapPreview, + animated: true, + thumbnailOnly: true, + ); + } + + bool isSendingImageInWeb(MatrixFile? matrixFile) { + return matrixFile != null && + matrixFile.bytes != null && + matrixFile is MatrixImageFile; + } + + bool isSendingImageInMobile(MatrixFile? matrixFile) { + return matrixFile != null && + matrixFile.filePath != null && + matrixFile is MatrixImageFile && + !PlatformInfos.isWeb; + } +} diff --git a/lib/pages/chat/events/sending_image_info_widget.dart b/lib/pages/chat/events/images_builder/sending_image_info_widget.dart similarity index 69% rename from lib/pages/chat/events/sending_image_info_widget.dart rename to lib/pages/chat/events/images_builder/sending_image_info_widget.dart index 1350b3d459..8ab17bc500 100644 --- a/lib/pages/chat/events/sending_image_info_widget.dart +++ b/lib/pages/chat/events/images_builder/sending_image_info_widget.dart @@ -3,8 +3,10 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/events/message_content_style.dart'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/presentation/model/file/display_image_info.dart'; +import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/interactive_viewer_gallery.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/hero_page_route.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart'; @@ -32,7 +34,7 @@ class SendingImageInfoWidget extends StatelessWidget { void _onTap(BuildContext context) async { if (onTapPreview != null) { - Navigator.of(context).push( + Navigator.of(context, rootNavigator: PlatformInfos.isWeb).push( HeroPageRoute( builder: (context) { return InteractiveViewerGallery( @@ -53,7 +55,6 @@ class SendingImageInfoWidget extends StatelessWidget { event.status == EventStatus.synced) { sendingFileProgressNotifier.value = 1; } - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; return Hero( tag: event.eventId, @@ -87,7 +88,8 @@ class SendingImageInfoWidget extends StatelessWidget { child: Stack( alignment: Alignment.center, children: [ - if (displayImageInfo.hasBlur) + if (matrixFile.bytes?.isNotEmpty != true || + matrixFile.filePath == null) SizedBox( width: MessageContentStyle.imageBubbleWidth( displayImageInfo.size.width, @@ -99,18 +101,30 @@ class SendingImageInfoWidget extends StatelessWidget { hash: event.blurHash ?? AppConfig.defaultImageBlurHash, ), ), - Image.file( - File(matrixFile.filePath!), - width: displayImageInfo.size.width, - height: displayImageInfo.size.height, - cacheHeight: - (displayImageInfo.size.height * devicePixelRatio) - .toInt(), - cacheWidth: (displayImageInfo.size.width * devicePixelRatio) - .toInt(), - fit: BoxFit.cover, - filterQuality: FilterQuality.low, - ), + if (!PlatformInfos.isWeb && matrixFile.filePath != null) + Image.file( + File(matrixFile.filePath!), + width: displayImageInfo.size.width, + height: displayImageInfo.size.height, + cacheHeight: + context.getCacheSize(displayImageInfo.size.height), + cacheWidth: + context.getCacheSize(displayImageInfo.size.width), + fit: BoxFit.cover, + filterQuality: FilterQuality.low, + ), + if (matrixFile.bytes?.isNotEmpty == true) + Image.memory( + matrixFile.bytes!, + width: displayImageInfo.size.width, + height: displayImageInfo.size.height, + cacheHeight: + context.getCacheSize(displayImageInfo.size.height), + cacheWidth: + context.getCacheSize(displayImageInfo.size.width), + fit: BoxFit.cover, + filterQuality: FilterQuality.none, + ), ], ), ), diff --git a/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart b/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart new file mode 100644 index 0000000000..df50019f02 --- /dev/null +++ b/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart @@ -0,0 +1,72 @@ +import 'package:fluffychat/pages/chat/events/images_builder/image_placeholder.dart'; +import 'package:fluffychat/pages/chat/events/message_content_style.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blurhash/flutter_blurhash.dart'; +import 'package:matrix/matrix.dart'; + +class UnencryptedImageWidget extends StatelessWidget { + const UnencryptedImageWidget({ + super.key, + required this.event, + required this.isThumbnail, + required this.width, + required this.height, + required this.fit, + }); + + final Event event; + final bool isThumbnail; + final double width; + final double height; + final BoxFit fit; + + @override + Widget build(BuildContext context) { + return Image.network( + event + .attachmentOrThumbnailMxcUrl(getThumbnail: isThumbnail)! + .getDownloadLink(event.room.client) + .toString(), + frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { + if (wasSynchronouslyLoaded) { + return child; + } + return AnimatedSwitcher( + duration: MessageContentStyle.animationSwitcherDuration, + child: frame != null + ? child + : ImagePlaceholder( + event: event, + width: width, + height: height, + fit: fit, + ), + ); + }, + fit: fit, + width: width, + height: height, + cacheWidth: (width * MediaQuery.devicePixelRatioOf(context)).round(), + cacheHeight: (height * MediaQuery.devicePixelRatioOf(context)).round(), + filterQuality: FilterQuality.medium, + errorBuilder: (context, error, stackTrace) { + return BlurHash( + hash: event.blurHash ?? MessageContentStyle.defaultBlurHash, + ); + }, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) { + return child; + } + return SizedBox( + width: width, + height: height, + child: BlurHash( + hash: event.blurHash ?? MessageContentStyle.defaultBlurHash, + ), + ); + }, + ); + } +} diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 9e367c14d8..5067a8bb55 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -1,13 +1,13 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/events/call_invite_content.dart'; import 'package:fluffychat/pages/chat/events/encrypted_content.dart'; +import 'package:fluffychat/pages/chat/events/images_builder/message_content_image_builder.dart'; import 'package:fluffychat/pages/chat/events/message_content_style.dart'; import 'package:fluffychat/pages/chat/events/message_download_content_web.dart'; import 'package:fluffychat/pages/chat/events/formatted_text_widget.dart'; import 'package:fluffychat/pages/chat/events/message_video_download_content.dart'; import 'package:fluffychat/pages/chat/events/message_video_download_content_web.dart'; import 'package:fluffychat/pages/chat/events/redacted_content.dart'; -import 'package:fluffychat/pages/chat/events/sending_image_info_widget.dart'; import 'package:fluffychat/pages/chat/events/sending_video_widget.dart'; import 'package:fluffychat/pages/chat/events/unknown_content.dart'; import 'package:fluffychat/presentation/model/file/display_image_info.dart'; @@ -26,7 +26,6 @@ import 'package:matrix/matrix.dart' hide Visibility; import 'audio_player.dart'; import 'cute_events.dart'; -import 'image_bubble.dart'; import 'map_bubble.dart'; import 'message_download_content.dart'; import 'sticker.dart'; @@ -62,7 +61,7 @@ class MessageContent extends StatelessWidget case EventTypes.Sticker: switch (event.messageType) { case MessageTypes.Image: - return _MessageImageBuilder( + return MessageImageBuilder( event: event, onTapPreview: onTapPreview, onTapSelectMode: onTapSelectMode, @@ -247,80 +246,6 @@ class MessageContent extends StatelessWidget } } -class _MessageImageBuilder extends StatelessWidget { - final Event event; - - final void Function()? onTapPreview; - - final void Function()? onTapSelectMode; - - const _MessageImageBuilder({ - required this.event, - this.onTapPreview, - this.onTapSelectMode, - }); - - @override - Widget build(BuildContext context) { - final matrixFile = event.getMatrixFile(); - - DisplayImageInfo? displayImageInfo = - event.getOriginalResolution()?.getDisplayImageInfo(context); - - if (isSendingImageInMobile(matrixFile)) { - final file = matrixFile as MatrixImageFile; - displayImageInfo = Size( - file.width?.toDouble() ?? MessageContentStyle.imageWidth(context), - file.height?.toDouble() ?? MessageContentStyle.imageHeight(context), - ).getDisplayImageInfo(context); - return SendingImageInfoWidget( - key: ValueKey(event.eventId), - matrixFile: file, - event: event, - onTapPreview: onTapPreview, - displayImageInfo: displayImageInfo, - ); - } - displayImageInfo ??= DisplayImageInfo( - size: Size( - MessageContentStyle.imageWidth(context), - MessageContentStyle.imageHeight(context), - ), - hasBlur: true, - ); - if (isSendingImageInWeb(matrixFile)) { - final file = matrixFile as MatrixImageFile; - displayImageInfo = Size( - file.width?.toDouble() ?? MessageContentStyle.imageWidth(context), - file.height?.toDouble() ?? MessageContentStyle.imageHeight(context), - ).getDisplayImageInfo(context); - } - return ImageBubble( - event, - width: displayImageInfo.size.width, - height: displayImageInfo.size.height, - fit: BoxFit.cover, - onTapSelectMode: onTapSelectMode, - onTapPreview: onTapPreview, - animated: true, - thumbnailOnly: true, - ); - } - - bool isSendingImageInWeb(MatrixFile? matrixFile) { - return matrixFile != null && - matrixFile.bytes != null && - matrixFile is MatrixImageFile; - } - - bool isSendingImageInMobile(MatrixFile? matrixFile) { - return matrixFile != null && - matrixFile.filePath != null && - matrixFile is MatrixImageFile && - !PlatformInfos.isWeb; - } -} - class _MessageVideoBuilder extends StatelessWidget { final Event event; diff --git a/lib/pages/chat/events/message_content_style.dart b/lib/pages/chat/events/message_content_style.dart index e12a46d20a..7837ca9d4d 100644 --- a/lib/pages/chat/events/message_content_style.dart +++ b/lib/pages/chat/events/message_content_style.dart @@ -10,6 +10,7 @@ class MessageContentStyle { static const int maxLengthTextInline = 180; static const double appBarFontSize = 16.0; + static Duration animationSwitcherDuration = const Duration(milliseconds: 300); static double imageWidth(BuildContext context) { if (responsiveUtils.isDesktop(context)) { diff --git a/lib/pages/chat/events/sticker.dart b/lib/pages/chat/events/sticker.dart index fe9dd0f534..5d573ecd48 100644 --- a/lib/pages/chat/events/sticker.dart +++ b/lib/pages/chat/events/sticker.dart @@ -5,7 +5,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; import '../../../config/app_config.dart'; -import 'image_bubble.dart'; +import 'images_builder/image_bubble.dart'; class Sticker extends StatefulWidget { final Event event; diff --git a/lib/pages/chat/send_file_dialog/media_page_view_widget.dart b/lib/pages/chat/send_file_dialog/media_page_view_widget.dart index 33e21eb7cb..8061be8a9b 100644 --- a/lib/pages/chat/send_file_dialog/media_page_view_widget.dart +++ b/lib/pages/chat/send_file_dialog/media_page_view_widget.dart @@ -61,6 +61,9 @@ class MediaPageViewWidget extends StatelessWidget { thumbnails[firstFile]?.bytes ?? Uint8List(0), fit: BoxFit.cover, + cacheWidth: (SendFileDialogStyle.imageSize * + MediaQuery.devicePixelRatioOf(context)) + .round(), ); }, ), diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index 73efe95da7..00246bd056 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; -import 'events/image_bubble.dart'; +import 'events/images_builder/image_bubble.dart'; class StickerPickerDialog extends StatefulWidget { final Room room; diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index eaa40b8a17..d654443575 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -16,12 +16,16 @@ class ImageViewer extends StatefulWidget { final Event? event; final Uint8List? imageData; final String? filePath; + final double? width; + final double? height; const ImageViewer({ Key? key, this.event, this.imageData, this.filePath, + this.width, + this.height, }) : super(key: key); @override @@ -135,5 +139,7 @@ class ImageViewerController extends State { this, imageData: widget.imageData, filePath: widget.filePath, + width: widget.width, + height: widget.height, ); } diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 58742ba159..dcdac7e06d 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -15,12 +15,16 @@ class ImageViewerView extends StatelessWidget { final ImageViewerController controller; final Uint8List? imageData; final String? filePath; + final double? width; + final double? height; const ImageViewerView( this.controller, { Key? key, this.imageData, this.filePath, + this.width, + this.height, }) : super(key: key); @override @@ -36,6 +40,8 @@ class ImageViewerView extends StatelessWidget { imageWidget = _ImageWidget( event: controller.widget.event!, controller: controller, + width: width, + height: height, ); } else if (imageData != null) { imageWidget = Image.memory( @@ -97,7 +103,16 @@ class _ImageWidget extends StatelessWidget { final Event event; - const _ImageWidget({required this.event, required this.controller}); + final double? width; + + final double? height; + + const _ImageWidget({ + required this.event, + required this.controller, + this.width, + this.height, + }); @override Widget build(BuildContext context) { @@ -110,7 +125,12 @@ class _ImageWidget extends StatelessWidget { if (snapshot.data == null || snapshot.data!.bytes?.isEmpty != false) { return const CircularProgressIndicator(); } - return Image.memory(snapshot.data!.bytes!); + return Image.memory( + snapshot.data!.bytes!, + cacheWidth: width != null + ? (width! * MediaQuery.devicePixelRatioOf(context)).toInt() + : null, + ); }, ); } else { diff --git a/lib/utils/extension/build_context_extension.dart b/lib/utils/extension/build_context_extension.dart index 4e7aaec9a0..bf07c743c1 100644 --- a/lib/utils/extension/build_context_extension.dart +++ b/lib/utils/extension/build_context_extension.dart @@ -71,19 +71,19 @@ extension ContextExtensionss on BuildContext { TextTheme get textTheme => Theme.of(this).textTheme; /// similar to [MediaQuery.of(context).padding] - EdgeInsets get mediaQueryPadding => MediaQuery.of(this).padding; + EdgeInsets get mediaQueryPadding => MediaQuery.paddingOf(this); /// similar to [MediaQuery.of(context).padding] MediaQueryData get mediaQuery => MediaQuery.of(this); /// similar to [MediaQuery.of(context).viewPadding] - EdgeInsets get mediaQueryViewPadding => MediaQuery.of(this).viewPadding; + EdgeInsets get mediaQueryViewPadding => MediaQuery.viewPaddingOf(this); /// similar to [MediaQuery.of(context).viewInsets] - EdgeInsets get mediaQueryViewInsets => MediaQuery.of(this).viewInsets; + EdgeInsets get mediaQueryViewInsets => MediaQuery.viewInsetsOf(this); /// similar to [MediaQuery.of(context).orientation] - Orientation get orientation => MediaQuery.of(this).orientation; + Orientation get orientation => MediaQuery.orientationOf(this); /// check if device is on landscape mode bool get isLandscape => orientation == Orientation.landscape; @@ -92,7 +92,7 @@ extension ContextExtensionss on BuildContext { bool get isPortrait => orientation == Orientation.portrait; /// similar to [MediaQuery.of(this).devicePixelRatio] - double get devicePixelRatio => MediaQuery.of(this).devicePixelRatio; + double get devicePixelRatio => MediaQuery.devicePixelRatioOf(this); /// get the shortestSide from screen double get mediaQueryShortestSide => mediaQuerySize.shortestSide; @@ -192,4 +192,8 @@ extension ContextExtensionss on BuildContext { void goToRoomWithEvent(String roomId, String eventId) { go('/rooms/$roomId?event=$eventId'); } + + int getCacheSize(double size) { + return (MediaQuery.devicePixelRatioOf(this) * size).round(); + } } diff --git a/lib/widgets/avatar/avatar.dart b/lib/widgets/avatar/avatar.dart index 8c6e2dd46a..ce22db6d0a 100644 --- a/lib/widgets/avatar/avatar.dart +++ b/lib/widgets/avatar/avatar.dart @@ -43,7 +43,7 @@ class Avatar extends StatelessWidget { fit: BoxFit.cover, width: size, height: size, - cacheWidth: (size * MediaQuery.of(context).devicePixelRatio).toInt(), + cacheWidth: (size * MediaQuery.devicePixelRatioOf(context)).round(), cacheKey: mxContent.toString(), placeholder: (context) => _fallbackAvatar(), ), diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index aae43264fc..d52d329cbc 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/presentation/enum/chat/media_viewer_popup_result_enum.dart'; +import 'package:fluffychat/utils/extension/build_context_extension.dart'; import 'package:fluffychat/utils/interactive_viewer_gallery.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/download_file_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -45,6 +46,8 @@ class MxcImage extends StatefulWidget { final int? cacheWidth; + final int? cacheHeight; + const MxcImage({ this.uri, this.event, @@ -68,6 +71,7 @@ class MxcImage extends StatefulWidget { this.noResize = false, this.closeRightColumn, this.cacheWidth, + this.cacheHeight, Key? key, }) : super(key: key); @@ -120,11 +124,10 @@ class _MxcImageState extends State { final event = widget.event; if (uri != null) { - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; final width = widget.width; - final realWidth = width == null ? null : width * devicePixelRatio; + final realWidth = width == null ? null : context.getCacheSize(width); final height = widget.height; - final realHeight = height == null ? null : height * devicePixelRatio; + final realHeight = height == null ? null : context.getCacheSize(height); final httpUri = widget.isThumbnail ? uri.getThumbnail( @@ -307,6 +310,7 @@ class _MxcImageState extends State { fit: widget.fit, needResize: needResize, cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, imageErrorWidgetBuilder: (context, __, ___) { _isCached = false; _imageData = null; @@ -327,6 +331,7 @@ class _ImageWidget extends StatelessWidget { final BoxFit? fit; final ImageErrorWidgetBuilder imageErrorWidgetBuilder; final int? cacheWidth; + final int? cacheHeight; const _ImageWidget({ this.filePath, @@ -337,11 +342,11 @@ class _ImageWidget extends StatelessWidget { this.fit, required this.imageErrorWidgetBuilder, this.cacheWidth, + this.cacheHeight, }); @override Widget build(BuildContext context) { - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; return filePath != null && filePath!.isNotEmpty ? Image.file( File(filePath!), @@ -350,11 +355,13 @@ class _ImageWidget extends StatelessWidget { cacheWidth: cacheWidth != null ? cacheWidth! : (width != null && needResize) - ? (width! * devicePixelRatio).toInt() + ? context.getCacheSize(width!) + : null, + cacheHeight: cacheHeight != null + ? cacheHeight! + : (height != null && needResize) + ? context.getCacheSize(height!) : null, - cacheHeight: (height != null && needResize) - ? (height! * devicePixelRatio).toInt() - : null, fit: fit, filterQuality: FilterQuality.medium, errorBuilder: imageErrorWidgetBuilder, @@ -367,11 +374,13 @@ class _ImageWidget extends StatelessWidget { cacheWidth: cacheWidth != null ? cacheWidth! : (width != null && needResize) - ? (width! * devicePixelRatio).toInt() + ? context.getCacheSize(width!) + : null, + cacheHeight: cacheHeight != null + ? cacheHeight! + : (height != null && needResize) + ? context.getCacheSize(height!) : null, - cacheHeight: (height != null && needResize) - ? (height! * devicePixelRatio).toInt() - : null, fit: fit, filterQuality: FilterQuality.medium, errorBuilder: imageErrorWidgetBuilder,