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

TW-823 Metadata width height thumbnail blurhash for file sharing #831

Merged
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
2 changes: 0 additions & 2 deletions lib/di/global/get_it_initializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import 'package:fluffychat/domain/usecase/search/pre_search_recent_contacts_inte
import 'package:fluffychat/domain/usecase/search/search_recent_chat_interactor.dart';
import 'package:fluffychat/domain/usecase/send_file_interactor.dart';
import 'package:fluffychat/domain/usecase/send_file_on_web_interactor.dart';
import 'package:fluffychat/domain/usecase/send_image_interactor.dart';
import 'package:fluffychat/domain/usecase/send_images_interactor.dart';
import 'package:fluffychat/domain/usecase/settings/update_profile_interactor.dart';
import 'package:fluffychat/event/twake_event_dispatcher.dart';
Expand Down Expand Up @@ -141,7 +140,6 @@ class GetItInitializer {
getIt.registerFactory<GetContactsInteractor>(
() => GetContactsInteractor(),
);
getIt.registerSingleton<SendImageInteractor>(SendImageInteractor());
getIt.registerSingleton<SendImagesInteractor>(SendImagesInteractor());
getIt.registerSingleton<DownloadFileForPreviewInteractor>(
DownloadFileForPreviewInteractor(),
Expand Down
12 changes: 12 additions & 0 deletions lib/domain/model/room/room_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,16 @@ extension RoomExtension on Room {
Future<void> unmute() async {
await setPushRuleState(PushRuleState.notify);
}

String storePlaceholderFileInMem({
required FileInfo fileInfo,
String? txid,
}) {
txid ??= client.generateUniqueTransactionId();
final matrixFile = MatrixFile.fromFileInfo(
fileInfo: fileInfo,
);
sendingFilePlaceholders[txid] = matrixFile;
return txid;
}
}
18 changes: 2 additions & 16 deletions lib/domain/usecase/send_file_interactor.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:fluffychat/data/network/extensions/file_info_extension.dart';
import 'package:fluffychat/domain/model/room/room_extension.dart';
import 'package:fluffychat/presentation/extensions/send_file_extension.dart';
import 'package:matrix/matrix.dart';

Expand All @@ -14,9 +15,8 @@ class SendFileInteractor {
}) async {
try {
for (final fileInfo in fileInfos) {
final txid = _storePlaceholderFileInMem(
final txid = room.storePlaceholderFileInMem(
fileInfo: fileInfo,
room: room,
);
await room.sendFileEvent(
fileInfo,
Expand All @@ -31,18 +31,4 @@ class SendFileInteractor {
Logs().d("SendFileInteractor: execute(): $error");
}
}

String _storePlaceholderFileInMem({
required Room room,
required FileInfo fileInfo,
}) {
final txid = room.client.generateUniqueTransactionId();
final matrixFile = MatrixFile.fromMimeType(
name: fileInfo.fileName,
filePath: fileInfo.filePath,
mimeType: fileInfo.mimeType,
);
room.sendingFilePlaceholders[txid] = matrixFile;
return txid;
}
}
30 changes: 0 additions & 30 deletions lib/domain/usecase/send_image_interactor.dart

This file was deleted.

2 changes: 1 addition & 1 deletion lib/domain/usecase/send_images_interactor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class SendImagesInteractor {
);
}
} catch (error) {
Logs().d("SendImageInteractor: execute(): $error");
Logs().d("SendImagesInteractor: execute(): $error");
}
}
}
4 changes: 3 additions & 1 deletion lib/pages/chat/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ class ChatController extends State<Chat>
sendFileInteractor.execute(
room: room!,
fileInfos: [
FileInfo(shareFile!.name, shareFile!.filePath!, shareFile!.size),
FileInfo.fromMatrixFile(
shareFile!,
)
],
);
}
Expand Down
15 changes: 8 additions & 7 deletions lib/pages/chat/events/message_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class _MessageImageBuilder extends StatelessWidget {
file.height?.toDouble() ?? MessageContentStyle.imageHeight(context),
).getDisplayImageInfo(context);
return SendingImageInfoWidget(
key: ValueKey(event.eventId),
matrixFile: file,
event: event,
onTapPreview: onTapPreview,
Expand Down Expand Up @@ -387,10 +388,12 @@ class _MessageVideoBuilder extends StatelessWidget {
}
if (isSendingVideo(matrixFile)) {
final file = matrixFile as MatrixVideoFile;
displayImageInfo = Size(
file.width!.toDouble(),
file.height!.toDouble(),
).getDisplayImageInfo(context);
if (file.width != null && file.height != null) {
displayImageInfo = Size(
file.width!.toDouble(),
file.height!.toDouble(),
).getDisplayImageInfo(context);
}
return SendingVideoWidget(
key: ValueKey(event.eventId),
event: event,
Expand All @@ -413,8 +416,6 @@ class _MessageVideoBuilder extends StatelessWidget {
}

bool isSendingVideo(MatrixFile? matrixFile) {
return matrixFile is MatrixVideoFile &&
matrixFile.width != null &&
matrixFile.height != null;
return matrixFile is MatrixVideoFile && matrixFile.bytes != null;
}
}
11 changes: 2 additions & 9 deletions lib/pages/chat_list/receive_sharing_intent_mixin.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import 'package:fluffychat/event/twake_event_types.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart';
import 'package:fluffychat/presentation/extensions/shared_media_file_extension.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/twake_app.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';

import 'package:fluffychat/config/app_config.dart';
import 'package:matrix/matrix.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:uni_links/uni_links.dart';

Expand All @@ -25,14 +23,9 @@ mixin ReceiveSharingIntentMixin<T extends StatefulWidget> on State<T> {
void _processIncomingSharedFiles(List<SharedMediaFile> files) {
if (files.isEmpty) return;
final shareFile = files.first;
final path = Uri.decodeFull(shareFile.path.replaceFirst('file://', ''));
final file = File(path);
matrixState.shareContent = {
'msgtype': TwakeEventTypes.shareFileEventType,
'file': MatrixFile(
name: file.path,
filePath: file.path,
).detectFileType,
'file': shareFile.toMatrixFile(),
};
TwakeApp.router.go('/share');
}
Expand Down
20 changes: 20 additions & 0 deletions lib/presentation/extensions/image_extension.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'dart:async';

import 'package:flutter/widgets.dart';

extension ImageExtension on Image {
Future<Size> calculateImageDimension() {
final completer = Completer<Size>();
image.resolve(const ImageConfiguration()).addListener(
ImageStreamListener(
(ImageInfo image, bool synchronousCall) {
final myImage = image.image;
final Size size =
Size(myImage.width.toDouble(), myImage.height.toDouble());
completer.complete(size);
},
),
);
return completer.future;
}
}
104 changes: 99 additions & 5 deletions lib/presentation/extensions/send_file_extension.dart
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import 'dart:async';
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:fluffychat/data/network/media/media_api.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/di/global/get_it_initializer.dart';
import 'package:fluffychat/domain/model/room/room_extension.dart';
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:flutter/widgets.dart';
import 'package:image/image.dart' as img;
import 'package:blurhash_dart/blurhash_dart.dart';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:video_thumbnail/video_thumbnail.dart';

typedef TransactionId = String;

Expand Down Expand Up @@ -89,8 +94,30 @@ extension SendFileExtension on Room {
fileInfo,
targetPath: tempThumbnailFile.path,
);
fileInfo = ImageFileInfo(
fileInfo.fileName,
fileInfo.filePath,
fileInfo.fileSize,
width: thumbnail?.width,
height: thumbnail?.height,
);
storePlaceholderFileInMem(
fileInfo: fileInfo,
txid: txid,
);
fakeImageEvent = await sendFakeImagePickerFileEvent(
fileInfo,
txid: txid,
messageType: msgType,
inReplyTo: inReplyTo,
editEventId: editEventId,
shrinkImageMaxDimension: shrinkImageMaxDimension,
extraContent: extraContent,
);

if (thumbnail != null && fileInfo.fileSize < thumbnail.fileSize) {
if (thumbnail != null &&
fileInfo.fileSize > 0 &&
fileInfo.fileSize < thumbnail.fileSize) {
thumbnail = null; // in this case, the thumbnail is not usefull
}
} else if (fileInfo is VideoFileInfo) {
Expand All @@ -100,6 +127,29 @@ extension SendFileExtension on Room {
FileSendingStatus.generatingThumbnail.name,
);
thumbnail ??= await _getThumbnailVideo(tempThumbnailFile, fileInfo);
if (fileInfo.width == null || fileInfo.height == null) {
fileInfo = VideoFileInfo(
fileInfo.fileName,
fileInfo.filePath,
fileInfo.fileSize,
imagePlaceholderBytes: fileInfo.imagePlaceholderBytes,
width: thumbnail.width,
height: thumbnail.height,
);
storePlaceholderFileInMem(
fileInfo: fileInfo,
txid: txid,
);
fakeImageEvent = await sendFakeImagePickerFileEvent(
fileInfo,
txid: txid,
messageType: msgType,
inReplyTo: inReplyTo,
editEventId: editEventId,
shrinkImageMaxDimension: shrinkImageMaxDimension,
extraContent: extraContent,
);
}
}

EncryptedFileInfo? encryptedFileInfo;
Expand Down Expand Up @@ -211,6 +261,7 @@ extension SendFileExtension on Room {
'url': uploadResp.toString(),
if (encryptedFileInfo != null) 'file': encryptedFileInfo.toJson(),
'info': {
...thumbnail?.metadata ?? {},
...fileInfo.metadata,
if (thumbnail != null && encryptedThumbnail == null)
'thumbnail_url': thumbnailUploadResp.toString(),
Expand Down Expand Up @@ -351,15 +402,26 @@ extension SendFileExtension on Room {
originalFile.filePath,
targetPath,
quality: AppConfig.thumbnailQuality,
format: CompressFormat.jpeg,
);
if (result == null) return null;
final size = await result.length();
var width = originalFile.width;
var height = originalFile.height;
if (width == null || height == null) {
final imageDimension = await runBenchmarked(
'_calculateImageDimension',
() => _calculateImageDimension(result.path),
);
width = imageDimension.width.toInt();
height = imageDimension.height.toInt();
}
return ImageFileInfo(
result.name,
result.path,
size,
width: originalFile.width,
height: originalFile.height,
width: width,
height: height,
);
} catch (e) {
Logs().e('Error while generating thumbnail', e);
Expand Down Expand Up @@ -392,12 +454,36 @@ extension SendFileExtension on Room {
File tempThumbnailFile,
VideoFileInfo fileInfo,
) async {
await tempThumbnailFile.writeAsBytes(fileInfo.imagePlaceholderBytes);
final int fileSize;
if (fileInfo.imagePlaceholderBytes.isNotEmpty) {
await tempThumbnailFile.writeAsBytes(fileInfo.imagePlaceholderBytes);
fileSize = fileInfo.imagePlaceholderBytes.lengthInBytes;
} else {
await VideoThumbnail.thumbnailFile(
video: fileInfo.filePath,
imageFormat: ImageFormat.JPEG,
quality: AppConfig.thumbnailQuality,
thumbnailPath: tempThumbnailFile.path,
);
fileSize = await tempThumbnailFile.length();
}
var width = fileInfo.width;
var height = fileInfo.height;
if (width == null || height == null) {
final imageDimension = await runBenchmarked(
'_calculateImageDimension',
() => _calculateImageBytesDimension(fileInfo.imagePlaceholderBytes),
);
width = imageDimension.width.toInt();
height = imageDimension.height.toInt();
}
Logs().d('Video thumbnail generated', tempThumbnailFile.path);
final newThumbnail = ImageFileInfo(
tempThumbnailFile.path.split("/").last,
tempThumbnailFile.path,
fileInfo.imagePlaceholderBytes.lengthInBytes,
fileSize,
width: width,
height: height,
);
return newThumbnail;
}
Expand All @@ -411,4 +497,12 @@ extension SendFileExtension on Room {
.unsigned![key] = value;
await handleImageFakeSync(fakeImageEvent);
}

Future<Size> _calculateImageDimension(String filePath) {
return Image.file(File(filePath)).calculateImageDimension();
}

Future<Size> _calculateImageBytesDimension(Uint8List bytes) {
return Image.memory(bytes).calculateImageDimension();
}
}
Loading