Skip to content

Commit

Permalink
TW-1806: fix image weird display on web (#1866)
Browse files Browse the repository at this point in the history
* TW-1806: add support mime files for web platform, and util method to getTwake supported mimeType

* TW-1806: make image have width and height when the error image when display

* TW-1806: update mime type when drag/drop, paste and add file

* TW-1806: fix wrong display when send HEIC file in android

* TW-1806: display avif file in web

* TW-1806: if image viewer can't be display by real image, use thumbnail

* Tw 1750/improve when upload video (#1875)

* TW-1750: add metadate when sending file event

* TW-1750: fix typo error int videoMimeTypes
  • Loading branch information
sherlockvn committed Jun 18, 2024
1 parent 62fe121 commit bdac697
Show file tree
Hide file tree
Showing 21 changed files with 420 additions and 69 deletions.
7 changes: 2 additions & 5 deletions lib/data/network/extensions/file_info_extension.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import 'package:flutter/foundation.dart';
import 'package:fluffychat/utils/mime_type_uitls.dart';
import 'package:matrix/matrix.dart';
import 'package:mime/mime.dart';

extension FileInfoExtension on FileInfo {
String get fileExtension => fileName.split('.').last;

String get mimeType =>
lookupMimeType(kIsWeb ? fileName : filePath) ??
'application/octet-stream';
String get mimeType => MimeTypeUitls.instance.getTwakeMimeType(filePath);

Map<String, dynamic> get metadata => ({
'mimetype': mimeType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:file_picker/file_picker.dart';
import 'package:fluffychat/utils/mime_type_uitls.dart';
import 'package:matrix/matrix.dart';

extension PlatformFileListExtension on PlatformFile {
Expand All @@ -9,6 +10,7 @@ extension PlatformFileListExtension on PlatformFile {
bytes: bytes,
name: name,
filePath: path ?? '$temporaryDirectoryPath/$name',
mimeType: MimeTypeUitls.instance.getTwakeMimeType(name),
readStream: readStream,
sizeInBytes: size,
);
Expand All @@ -21,6 +23,7 @@ extension PlatformFileListExtension on PlatformFile {
filePath: '',
readStream: readStream,
sizeInBytes: size,
mimeType: MimeTypeUitls.instance.getTwakeMimeType(name),
);
}

Expand Down
25 changes: 25 additions & 0 deletions lib/domain/model/preview_file/supported_preview_file_types.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:fluffychat/resource/image_paths.dart';
import 'package:fluffychat/utils/platform_infos.dart';

enum SupportedIconFileTypesEnum {
image,
Expand Down Expand Up @@ -40,14 +41,38 @@ class SupportedPreviewFileTypes {
static const imageMimeTypes = [
'image/bmp',
'image/jpeg',
'image/jpg',
'image/gif',
'image/png',
];

static const imageMimeTypesAndroid = [
...imageMimeTypes,
'image/webp',
];

static const imageMimeTypesIOS = [
...imageMimeTypes,
'image/heic',
'image/heif',
];

static List<String> get crossPlatformImageMimeTypes {
if (PlatformInfos.isAndroid) {
return imageMimeTypesAndroid;
} else if (PlatformInfos.isIOS) {
return imageMimeTypesIOS;
} else {
return imageMimeTypes;
}
}

static const videoMimeTypes = [
'video/mp4',
'video/3gpp',
'video/quicktime',
'video/mov',
'video/mpeg',
];

static const audioMimeTypes = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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/extension/mime_type_extension.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';
import 'package:flutter_avif/flutter_avif.dart';

class UnencryptedImageWidget extends StatelessWidget {
const UnencryptedImageWidget({
Expand All @@ -23,6 +25,17 @@ class UnencryptedImageWidget extends StatelessWidget {

@override
Widget build(BuildContext context) {
if (event.mimeType == TwakeMimeTypeExtension.avifMimeType) {
return AvifImage.network(
event
.attachmentOrThumbnailMxcUrl(getThumbnail: isThumbnail)!
.getDownloadLink(event.room.client)
.toString(),
height: height,
width: width,
fit: BoxFit.cover,
);
}
return Image.network(
event
.attachmentOrThumbnailMxcUrl(getThumbnail: isThumbnail)!
Expand Down Expand Up @@ -51,8 +64,22 @@ class UnencryptedImageWidget extends StatelessWidget {
cacheHeight: (height * MediaQuery.devicePixelRatioOf(context)).round(),
filterQuality: FilterQuality.medium,
errorBuilder: (context, error, stackTrace) {
return BlurHash(
hash: event.blurHash ?? MessageContentStyle.defaultBlurHash,
return Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: width,
height: height,
child: BlurHash(
hash: event.blurHash ?? MessageContentStyle.defaultBlurHash,
),
),
Icon(
Icons.error,
size: MessageContentStyle.iconErrorSize,
color: Theme.of(context).colorScheme.onError,
),
],
);
},
loadingBuilder: (context, child, loadingProgress) {
Expand Down
2 changes: 2 additions & 0 deletions lib/pages/chat/events/message_content_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,6 @@ class MessageContentStyle {
);

static const blurhashSize = 32;

static const iconErrorSize = 36.0;
}
28 changes: 28 additions & 0 deletions lib/pages/image_viewer/image_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class ImageViewerController extends State<ImageViewer> {

String? filePath;

String? thumbnailFilePath;

final downloadMediaFileInteractor = getIt.get<DownloadMediaFileInteractor>();

StreamSubscription? streamSubcription;
Expand All @@ -51,6 +53,7 @@ class ImageViewerController extends State<ImageViewer> {
super.initState();
if (!PlatformInfos.isWeb && widget.event != null) {
handleDownloadFile(widget.event!);
handleDownloadThumbnailFile(widget.event!);
}
}

Expand Down Expand Up @@ -78,6 +81,31 @@ class ImageViewerController extends State<ImageViewer> {
}
}

Future<void> handleDownloadThumbnailFile(Event event) async {
try {
streamSubcription = downloadMediaFileInteractor
.execute(event: event, getThumbnail: true)
.listen((state) {
state.fold(
(failure) {
if (failure is DownloadMediaFileFailure) {
Logs().e('Error downloading file', failure.exception);
}
},
(success) {
if (success is DownloadMediaFileSuccess) {
setState(() {
thumbnailFilePath = success.filePath;
});
}
},
);
});
} catch (e) {
Logs().e('Error downloading file', e);
}
}

@override
void dispose() {
streamSubcription?.cancel();
Expand Down
29 changes: 29 additions & 0 deletions lib/pages/image_viewer/image_viewer_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import 'dart:typed_data';

import 'package:fluffychat/pages/image_viewer/image_viewer_style.dart';
import 'package:fluffychat/pages/image_viewer/media_viewer_app_bar.dart';
import 'package:fluffychat/utils/extension/mime_type_extension.dart';
import 'package:fluffychat/utils/extension/value_notifier_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_avif/flutter_avif.dart';
import 'package:matrix/matrix.dart';

import 'image_viewer.dart';
Expand Down Expand Up @@ -117,6 +120,17 @@ class _ImageWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (PlatformInfos.isWeb) {
if (event.mimeType == TwakeMimeTypeExtension.avifMimeType) {
return AvifImage.network(
event
.attachmentOrThumbnailMxcUrl()!
.getDownloadLink(event.room.client)
.toString(),
height: height,
width: width,
fit: BoxFit.cover,
);
}
return FutureBuilder(
future: event.downloadAndDecryptAttachment(
getThumbnail: true,
Expand All @@ -135,10 +149,25 @@ class _ImageWidget extends StatelessWidget {
);
} else {
if (controller.filePath != null) {
if (event.mimeType == TwakeMimeTypeExtension.avifMimeType) {
return AvifImage.file(
File(controller.filePath!),
height: height,
width: width,
fit: BoxFit.cover,
);
}
return Image.file(
File(controller.filePath!),
fit: BoxFit.contain,
filterQuality: FilterQuality.none,
errorBuilder: (context, error, stackTrace) {
return Image.file(
File(controller.thumbnailFilePath!),
fit: BoxFit.contain,
filterQuality: FilterQuality.none,
);
},
);
} else {
return const CupertinoActivityIndicator(
Expand Down
23 changes: 22 additions & 1 deletion lib/presentation/extensions/send_file_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:fluffychat/presentation/extensions/image_extension.dart';
import 'package:fluffychat/presentation/fake_sending_file_info.dart';
import 'package:fluffychat/presentation/model/file/file_asset_entity.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/extension/mime_type_extension.dart';
import 'package:fluffychat/utils/manager/storage_directory_manager.dart';
import 'package:flutter/widgets.dart';
import 'package:image/image.dart' as img;
Expand Down Expand Up @@ -70,8 +71,25 @@ extension SendFileExtension on Room {
rethrow;
}

final encryptedService = EncryptedService();
final tempDir = await getTemporaryDirectory();

if (TwakeMimeTypeExtension.heicMimeTypes.contains(fileInfo.mimeType) &&
fileInfo is ImageFileInfo) {
final formattedDateTime = DateTime.now().getFormattedCurrentDateTime();
final targetPath =
await File('${tempDir.path}/$formattedDateTime${fileInfo.fileName}')
.create();
await _generateThumbnail(fileInfo, targetPath: targetPath.path);
fileInfo = ImageFileInfo(
fileInfo.fileName,
targetPath.path,
await targetPath.length(),
width: fileInfo.width,
height: fileInfo.height,
);
}

final encryptedService = EncryptedService();
final formattedDateTime = DateTime.now().getFormattedCurrentDateTime();
final tempEncryptedFile =
await File('${tempDir.path}/$formattedDateTime${fileInfo.fileName}')
Expand Down Expand Up @@ -358,6 +376,9 @@ extension SendFileExtension on Room {
'msgtype': messageType,
'body': fileInfo.fileName,
'filename': fileInfo.fileName,
'info': {
...fileInfo.metadata,
},
},
type: EventTypes.Message,
eventId: txid,
Expand Down
8 changes: 5 additions & 3 deletions lib/presentation/mixins/paste_image_mixin.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:fluffychat/pages/chat/send_file_dialog/send_file_dialog.dart';
import 'package:fluffychat/presentation/enum/chat/send_media_with_caption_status_enum.dart';
import 'package:fluffychat/utils/clipboard.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart';
import 'package:fluffychat/utils/mime_type_uitls.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/twake_snackbar.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -35,11 +37,11 @@ mixin PasteImageMixin {
(matrixFile) => matrixFile != null,
)
.map(
(matrixFile) => MatrixImageFile(
(matrixFile) => MatrixFile(
name: matrixFile!.name,
mimeType: matrixFile.mimeType,
mimeType: MimeTypeUitls.instance.getTwakeMimeType(matrixFile.name),
bytes: matrixFile.bytes,
),
).detectFileType,
)
.cast<MatrixImageFile>()
.toList();
Expand Down
15 changes: 6 additions & 9 deletions lib/presentation/mixins/send_files_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,12 @@ mixin SendFilesMixin {
allowMultiple: true,
);
final temporaryDirectory = await getTemporaryDirectory();
fileInfos ??= result?.files
.map(
(xFile) => FileInfo.fromMatrixFile(
xFile.toMatrixFileOnMobile(
temporaryDirectoryPath: temporaryDirectory.path,
),
),
)
.toList();
fileInfos ??= result?.files.map((xFile) {
final matrixFile = xFile.toMatrixFileOnMobile(
temporaryDirectoryPath: temporaryDirectory.path,
);
return FileInfo.fromMatrixFile(matrixFile);
}).toList();

if (fileInfos == null || fileInfos.isEmpty == true) return;
onSendFileCallback?.call();
Expand Down
20 changes: 18 additions & 2 deletions lib/utils/extension/mime_type_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ import 'package:matrix/matrix.dart';
typedef TwakeMimeType = String?;

extension TwakeMimeTypeExtension on TwakeMimeType {
static const String defaultUnsupportedImageMimeType = 'file/image';

static const String defaultUnsupportedVideoMimeType = 'file/video';

bool isAndroidSupportedPreview() =>
SupportedPreviewFileTypes.androidSupportedTypes.contains(this);

bool isIOSSupportedPreview() =>
SupportedPreviewFileTypes.iOSSupportedTypes.containsKey(this);

bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(this);
bool isImageFile() =>
SupportedPreviewFileTypes.imageMimeTypes.contains(this) ||
defaultUnsupportedImageMimeType == this;

bool isDocFile({String? fileType}) =>
SupportedPreviewFileTypes.docMimeTypes.contains(this) ||
Expand All @@ -39,7 +45,10 @@ extension TwakeMimeTypeExtension on TwakeMimeType {
SupportedPreviewFileTypes.zipFileTypes
.contains(fileType.toLowerCase());

bool isVideoFile() => SupportedPreviewFileTypes.videoMimeTypes.contains(this);
bool isVideoFile() {
return SupportedPreviewFileTypes.videoMimeTypes.contains(this) ||
defaultUnsupportedVideoMimeType == this;
}

bool isPdfFile({String? fileType}) =>
SupportedPreviewFileTypes.pdfMimeTypes.contains(this) ||
Expand Down Expand Up @@ -96,4 +105,11 @@ extension TwakeMimeTypeExtension on TwakeMimeType {
return L10n.of(context)!.file.toUpperCase();
}
}

static const String avifMimeType = 'image/avif';

static const List<String> heicMimeTypes = [
'image/heic',
'image/heif',
];
}
Loading

0 comments on commit bdac697

Please sign in to comment.