Skip to content

Commit

Permalink
TW-400 Memory Improve Image message in place holder while sending image
Browse files Browse the repository at this point in the history
  • Loading branch information
drminh2807 committed Aug 29, 2023
1 parent 2ad2426 commit 3983868
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 4 deletions.
86 changes: 84 additions & 2 deletions lib/presentation/extensions/send_file_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:fluffychat/presentation/model/file/file_asset_entity.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';

typedef TransactionId = String;

Expand All @@ -27,6 +28,7 @@ extension SendFileExtension on Room {
Event? inReplyTo,
String? editEventId,
int? shrinkImageMaxDimension,
FileInfo? thumbnail,
Map<String, dynamic>? extraContent,
}) async {
FileInfo tempfileInfo = fileInfo;
Expand Down Expand Up @@ -65,7 +67,32 @@ extension SendFileExtension on Room {
final tempEncryptedFile =
await File('${tempDir.path}/$formattedDateTime${fileInfo.fileName}')
.create();
final tempThumbnailFile = await File(
'${tempDir.path}/$formattedDateTime${fileInfo.fileName}_thumbnail.jpg',
).create();
final tempEncryptedThumbnailFile = await File(
'${tempDir.path}/$formattedDateTime${fileInfo.fileName}_encrypted_thumbnail',
).create();

// computing the thumbnail in case we can
if (fileInfo is ImageFileInfo &&
(thumbnail == null || shrinkImageMaxDimension != null)) {
fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] =
FileSendingStatus.generatingThumbnail.name;
await handleImageFakeSync(fakeImageEvent);
thumbnail ??= await _generateThumbnail(
fileInfo,
targetPath: tempThumbnailFile.path,
);

if (thumbnail != null && fileInfo.fileSize < thumbnail.fileSize) {
thumbnail = null; // in this case, the thumbnail is not usefull
}
}

EncryptedFileInfo? encryptedFileInfo;
EncryptedFileInfo? encryptedThumbnail;
if (encrypted && client.fileEncryptionEnabled) {
fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] = FileSendingStatus.encrypting.name;
Expand All @@ -80,18 +107,38 @@ extension SendFileExtension on Room {
tempEncryptedFile.path,
fileInfo.fileSize,
);
if (thumbnail != null) {
encryptedThumbnail = await encryptedService.encryptFile(
fileInfo: thumbnail,
outputFile: tempEncryptedThumbnailFile,
);
}
}
Uri? uploadResp;
Uri? uploadResp, thumbnailUploadResp;

fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] = FileSendingStatus.uploading.name;
while (uploadResp == null) {
while (uploadResp == null ||
(encryptedThumbnail != null && thumbnailUploadResp == null)) {
try {
final uploadFileApi = getIt.get<UploadFileAPI>();
final response = await uploadFileApi.uploadFile(fileInfo: tempfileInfo);
if (response.contentUri != null) {
uploadResp = Uri.parse(response.contentUri!);
}
if (uploadResp != null &&
encryptedThumbnail != null &&
thumbnail != null) {
final thumbnailResponse = await uploadFileApi.uploadFile(
fileInfo: FileInfo(
thumbnail.fileName,
tempEncryptedThumbnailFile.path,
thumbnail.fileSize,
),
);
thumbnailUploadResp =
Uri.tryParse(thumbnailResponse.contentUri ?? "");
}
} on MatrixException catch (e) {
fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
Expand Down Expand Up @@ -119,6 +166,17 @@ extension SendFileExtension on Room {
Logs().d('RoomExtension::EncryptedFileInfo: $encryptedFileInfo');
}

if (encryptedThumbnail != null) {
encryptedThumbnail = EncryptedFileInfo(
key: encryptedThumbnail.key,
version: encryptedThumbnail.version,
initialVector: encryptedThumbnail.initialVector,
hashes: encryptedThumbnail.hashes,
url: thumbnailUploadResp.toString(),
);
Logs().d('RoomExtension::EncryptedThumbnail: $encryptedThumbnail');
}

// Send event
final content = <String, dynamic>{
'msgtype': msgType,
Expand All @@ -128,6 +186,11 @@ extension SendFileExtension on Room {
if (encryptedFileInfo != null) 'file': encryptedFileInfo.toJson(),
'info': {
...fileInfo.metadata,
if (thumbnail != null && encryptedThumbnail == null)
'thumbnail_url': thumbnailUploadResp.toString(),
if (thumbnail != null && encryptedThumbnail != null)
'thumbnail_file': encryptedThumbnail.toJson(),
if (thumbnail != null) 'thumbnail_info': thumbnail.metadata,
},
if (extraContent != null) ...extraContent,
};
Expand Down Expand Up @@ -247,4 +310,23 @@ extension SendFileExtension on Room {
User? getUser(mxId) {
return getParticipants().firstWhereOrNull((user) => user.id == mxId);
}

Future<FileInfo?> _generateThumbnail(
ImageFileInfo originalFile, {
required String targetPath,
}) async {
try {
final result = await FlutterImageCompress.compressAndGetFile(
originalFile.filePath,
targetPath,
quality: 70,
);
if (result == null) return null;
final size = await result.length();
return FileInfo(result.name, result.path, size);
} catch (e) {
Logs().e('Error while generating thumbnail', e);
return null;
}
}
}
69 changes: 68 additions & 1 deletion lib/presentation/extensions/send_file_web_extension.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:matrix/matrix.dart';

extension SendFileWebExtension on Room {
Expand All @@ -10,6 +11,7 @@ extension SendFileWebExtension on Room {
Event? inReplyTo,
String? editEventId,
int? shrinkImageMaxDimension,
MatrixFile? thumbnail,
Map<String, dynamic>? extraContent,
}) async {
txid ??= client.generateUniqueTransactionId();
Expand Down Expand Up @@ -39,18 +41,39 @@ extension SendFileWebExtension on Room {
await handleImageFakeSync(fakeImageEvent);
rethrow;
}
// computing the thumbnail in case we can
if (file.msgType == MessageTypes.Image &&
(thumbnail == null || shrinkImageMaxDimension != null)) {
fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] =
FileSendingStatus.generatingThumbnail.name;
await handleImageFakeSync(fakeImageEvent);
thumbnail ??= await _generateThumbnail(file);
if (thumbnail != null && file.size < thumbnail.size) {
thumbnail = null; // in this case, the thumbnail is not usefull
}
}

EncryptedFile? encryptedFile;
MatrixFile? uploadFile;
MatrixFile? uploadThumbnail = thumbnail;
EncryptedFile? encryptedThumbnail;
if (encrypted && client.fileEncryptionEnabled) {
fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] = FileSendingStatus.encrypting.name;
await handleImageFakeSync(fakeImageEvent);
encryptedFile = await file.encrypt();
uploadFile =
MatrixFile.fromMimeType(bytes: encryptedFile!.data, name: 'crypt');
if (thumbnail != null) {
encryptedThumbnail = await thumbnail.encrypt();
uploadThumbnail = MatrixFile.fromMimeType(
bytes: encryptedThumbnail?.data,
name: 'crypt',
);
}
}
Uri? uploadResp;
Uri? uploadResp, thumbnailUploadResp;

fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] = FileSendingStatus.uploading.name;
Expand All @@ -62,6 +85,14 @@ extension SendFileWebExtension on Room {
filename: uploadFile.name,
contentType: uploadFile.mimeType,
);
thumbnailUploadResp =
uploadThumbnail != null && uploadThumbnail.bytes != null
? await client.uploadContent(
uploadThumbnail.bytes!,
filename: uploadThumbnail.name,
contentType: uploadThumbnail.mimeType,
)
: null;
} on MatrixException catch (e) {
fakeImageEvent.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
Expand Down Expand Up @@ -101,6 +132,24 @@ extension SendFileWebExtension on Room {
},
'info': {
...file.info,
if (thumbnail != null && encryptedThumbnail == null)
'thumbnail_url': thumbnailUploadResp.toString(),
if (thumbnail != null && encryptedThumbnail != null)
'thumbnail_file': {
'url': thumbnailUploadResp.toString(),
'mimetype': thumbnail.mimeType,
'v': 'v2',
'key': {
'alg': 'A256CTR',
'ext': true,
'k': encryptedThumbnail.k,
'key_ops': ['encrypt', 'decrypt'],
'kty': 'oct'
},
'iv': encryptedThumbnail.iv,
'hashes': {'sha256': encryptedThumbnail.sha256}
},
if (thumbnail != null) 'thumbnail_info': thumbnail.info,
},
if (extraContent != null) ...extraContent,
};
Expand Down Expand Up @@ -175,4 +224,22 @@ extension SendFileWebExtension on Room {
await client.handleSync(fakeImageEvent, direction: direction);
}
}

Future<MatrixFile?> _generateThumbnail(MatrixFile originalFile) async {
if (originalFile.bytes == null) return null;
try {
final result = await FlutterImageCompress.compressWithList(
originalFile.bytes!,
quality: 70,
);
return MatrixFile(
bytes: result,
name: originalFile.name,
mimeType: originalFile.mimeType,
);
} catch (e) {
Logs().e('Error while generating thumbnail', e);
return null;
}
}
}
4 changes: 3 additions & 1 deletion lib/widgets/mxc_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ class _MxcImageState extends State<MxcImage>
MaterialLocalizations.of(context).modalBarrierDismissLabel,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (_, animationOne, animationTwo) =>
ImageViewer(widget.event!, imageData: _imageData),
ImageViewer(widget.event!),
);
} else if (widget.onTapSelectMode != null) {
widget.onTapSelectMode!();
Expand Down Expand Up @@ -231,6 +231,8 @@ class _MxcImageState extends State<MxcImage>
data,
width: widget.width,
height: widget.height,
cacheWidth: widget.width?.toInt(),
cacheHeight: widget.height?.toInt(),
fit: widget.fit,
filterQuality: FilterQuality.medium,
errorBuilder: (context, __, ___) {
Expand Down
32 changes: 32 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.0"
flutter_image_compress:
dependency: "direct main"
description:
name: flutter_image_compress
sha256: "2725cce5c58fdeaf1db8f4203688228bb67e3523a66305ccaa6f99071beb6dc2"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
flutter_image_compress_common:
dependency: transitive
description:
name: flutter_image_compress_common
sha256: "8e7299afe109dc4b97fda34bf0f4967cc1fc10bc8050c374d449cab262d095b3"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
flutter_image_compress_platform_interface:
dependency: transitive
description:
name: flutter_image_compress_platform_interface
sha256: "3c7e86da7540b1adfa919b461885a41a018d4a26544d0fcbeaa769f6542e603d"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
flutter_image_compress_web:
dependency: transitive
description:
name: flutter_image_compress_web
sha256: e879189dc7f246dcf8f06c07ee849231341508bf51e8ed7d5dcbe778ddde0e81
url: "https://pub.dev"
source: hosted
version: "0.1.3+1"
flutter_inappwebview:
dependency: "direct main"
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ dependencies:
async: ^2.11.0
cached_network_image: ^3.2.3
pull_to_refresh: ^2.0.0
flutter_image_compress: ^2.0.4
dev_dependencies:
build_runner: ^2.3.3
dart_code_metrics: ^5.7.3
Expand Down
1 change: 1 addition & 0 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,6 @@
}
</script>
<script src="assets/assets/js/package/olm.js"></script>
<script src="https://unpkg.com/pica/dist/pica.min.js" ></script>
</body>
</html>

0 comments on commit 3983868

Please sign in to comment.