mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-27 23:09:35 +01:00
Merge branch 'krille/matrix-sdk-image-thumbnailing' into 'main'
fix: Set image width and height See merge request famedly/fluffychat!708
This commit is contained in:
commit
0a909dcfb9
@ -13,7 +13,6 @@ import 'package:fluffychat/pages/add_story/add_story_view.dart';
|
|||||||
import 'package:fluffychat/pages/add_story/invite_story_page.dart';
|
import 'package:fluffychat/pages/add_story/invite_story_page.dart';
|
||||||
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
|
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
|
||||||
import 'package:fluffychat/utils/resize_image.dart';
|
import 'package:fluffychat/utils/resize_image.dart';
|
||||||
import 'package:fluffychat/utils/room_send_file_extension.dart';
|
|
||||||
import 'package:fluffychat/utils/string_color.dart';
|
import 'package:fluffychat/utils/string_color.dart';
|
||||||
import 'package:fluffychat/widgets/matrix.dart';
|
import 'package:fluffychat/widgets/matrix.dart';
|
||||||
import '../../utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
|
import '../../utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
|
||||||
@ -29,8 +28,8 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
final TextEditingController controller = TextEditingController();
|
final TextEditingController controller = TextEditingController();
|
||||||
late Color backgroundColor;
|
late Color backgroundColor;
|
||||||
late Color backgroundColorDark;
|
late Color backgroundColorDark;
|
||||||
MatrixFile? image;
|
MatrixImageFile? image;
|
||||||
MatrixFile? video;
|
MatrixVideoFile? video;
|
||||||
|
|
||||||
VideoPlayerController? videoPlayerController;
|
VideoPlayerController? videoPlayerController;
|
||||||
|
|
||||||
@ -49,8 +48,13 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
);
|
);
|
||||||
final fileName = picked.fileName;
|
final fileName = picked.fileName;
|
||||||
if (fileName == null) return;
|
if (fileName == null) return;
|
||||||
|
final shrinked = await MatrixImageFile.shrink(
|
||||||
|
bytes: picked.toUint8List(),
|
||||||
|
name: fileName,
|
||||||
|
compute: Matrix.of(context).client.runInBackground,
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
image = MatrixFile(bytes: picked.toUint8List(), name: fileName);
|
image = shrinked;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +64,13 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
);
|
);
|
||||||
if (picked == null) return;
|
if (picked == null) return;
|
||||||
final bytes = await picked.readAsBytes();
|
final bytes = await picked.readAsBytes();
|
||||||
|
final shrinked = await MatrixImageFile.shrink(
|
||||||
|
bytes: bytes,
|
||||||
|
name: picked.name,
|
||||||
|
compute: Matrix.of(context).client.runInBackground,
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
image = MatrixFile(bytes: bytes, name: picked.name);
|
image = shrinked;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +82,7 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
final bytes = await picked.readAsBytes();
|
final bytes = await picked.readAsBytes();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
video = MatrixFile(bytes: bytes, name: picked.name);
|
video = MatrixVideoFile(bytes: bytes, name: picked.name);
|
||||||
videoPlayerController = VideoPlayerController.file(File(picked.path))
|
videoPlayerController = VideoPlayerController.file(File(picked.path))
|
||||||
..setLooping(true);
|
..setLooping(true);
|
||||||
});
|
});
|
||||||
@ -109,16 +118,17 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
var video = this.video?.detectFileType;
|
var video = this.video?.detectFileType;
|
||||||
if (video != null) {
|
if (video != null) {
|
||||||
video = await video.resizeVideo();
|
video = await video.resizeVideo();
|
||||||
await storiesRoom.sendFileEventWithThumbnail(
|
final thumbnail = await video.getVideoThumbnail();
|
||||||
|
await storiesRoom.sendFileEvent(
|
||||||
video,
|
video,
|
||||||
extraContent: {'body': controller.text},
|
extraContent: {'body': controller.text},
|
||||||
|
thumbnail: thumbnail,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var image = this.image?.detectFileType;
|
final image = this.image;
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
image = await image.resizeImage();
|
await storiesRoom.sendFileEvent(
|
||||||
await storiesRoom.sendFileEventWithThumbnail(
|
|
||||||
image,
|
image,
|
||||||
extraContent: {'body': controller.text},
|
extraContent: {'body': controller.text},
|
||||||
);
|
);
|
||||||
@ -142,9 +152,10 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
final shareContent = Matrix.of(context).shareContent;
|
final shareContent = Matrix.of(context).shareContent;
|
||||||
// ignore: unnecessary_null_comparison
|
// ignore: unnecessary_null_comparison
|
||||||
if (shareContent != null) {
|
if (shareContent != null) {
|
||||||
image = shareContent.tryGet<MatrixFile>('file');
|
final shareFile = shareContent.tryGet<MatrixFile>('file')?.detectFileType;
|
||||||
|
|
||||||
controller.text = shareContent.tryGet<String>('body') ?? '';
|
controller.text = shareContent.tryGet<String>('body') ?? '';
|
||||||
if (shareContent.tryGet<String>('msgtype') == MessageTypes.Image) {
|
if (shareFile is MatrixImageFile) {
|
||||||
Event(
|
Event(
|
||||||
content: shareContent,
|
content: shareContent,
|
||||||
type: EventTypes.Message,
|
type: EventTypes.Message,
|
||||||
@ -154,10 +165,10 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
).downloadAndDecryptAttachment().then((file) {
|
).downloadAndDecryptAttachment().then((file) {
|
||||||
setState(() {
|
setState(() {
|
||||||
image = file;
|
image = shareFile;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (shareContent.tryGet<String>('msgtype') == MessageTypes.Video) {
|
} else if (shareFile is MatrixVideoFile) {
|
||||||
Event(
|
Event(
|
||||||
content: shareContent,
|
content: shareContent,
|
||||||
type: EventTypes.Message,
|
type: EventTypes.Message,
|
||||||
@ -167,7 +178,7 @@ class AddStoryController extends State<AddStoryPage> {
|
|||||||
originServerTs: DateTime.now(),
|
originServerTs: DateTime.now(),
|
||||||
).downloadAndDecryptAttachment().then((file) {
|
).downloadAndDecryptAttachment().then((file) {
|
||||||
setState(() {
|
setState(() {
|
||||||
video = file;
|
video = shareFile;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import 'package:matrix/matrix.dart';
|
|||||||
|
|
||||||
import '../../utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
|
import '../../utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
|
||||||
import '../../utils/resize_image.dart';
|
import '../../utils/resize_image.dart';
|
||||||
import '../../utils/room_send_file_extension.dart';
|
|
||||||
|
|
||||||
class SendFileDialog extends StatefulWidget {
|
class SendFileDialog extends StatefulWidget {
|
||||||
final Room room;
|
final Room room;
|
||||||
@ -31,15 +30,21 @@ class _SendFileDialogState extends State<SendFileDialog> {
|
|||||||
|
|
||||||
Future<void> _send() async {
|
Future<void> _send() async {
|
||||||
var file = widget.file;
|
var file = widget.file;
|
||||||
|
MatrixImageFile? thumbnail;
|
||||||
if (file is MatrixImageFile &&
|
if (file is MatrixImageFile &&
|
||||||
!origImage &&
|
!origImage &&
|
||||||
file.bytes.length > minSizeToCompress) {
|
file.bytes.length > minSizeToCompress) {
|
||||||
file = await file.resizeImage();
|
file = await MatrixImageFile.shrink(
|
||||||
|
bytes: file.bytes,
|
||||||
|
name: file.name,
|
||||||
|
compute: widget.room.client.runInBackground,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (file is MatrixVideoFile && file.bytes.length > minSizeToCompress) {
|
if (file is MatrixVideoFile && file.bytes.length > minSizeToCompress) {
|
||||||
file = await file.resizeVideo();
|
file = await file.resizeVideo();
|
||||||
|
thumbnail = await file.getVideoThumbnail();
|
||||||
}
|
}
|
||||||
await widget.room.sendFileEventWithThumbnail(file);
|
await widget.room.sendFileEvent(file, thumbnail: thumbnail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -7,7 +7,6 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
|
|||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
import 'package:vrouter/vrouter.dart';
|
import 'package:vrouter/vrouter.dart';
|
||||||
|
|
||||||
import '../../utils/resize_image.dart';
|
|
||||||
import '../../widgets/matrix.dart';
|
import '../../widgets/matrix.dart';
|
||||||
import 'settings_emotes_view.dart';
|
import 'settings_emotes_view.dart';
|
||||||
|
|
||||||
@ -206,14 +205,19 @@ class EmotesSettingsController extends State<EmotesSettings> {
|
|||||||
name: result.fileName!,
|
name: result.fileName!,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
file = await file.resizeImage(calcBlurhash: false);
|
file = (await file.generateThumbnail(
|
||||||
|
compute: Matrix.of(context).client.runInBackground,
|
||||||
|
))!;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
final uploadResp = await showFutureLoadingDialog(
|
final uploadResp = await showFutureLoadingDialog(
|
||||||
context: context,
|
context: context,
|
||||||
future: () => Matrix.of(context).client.uploadContent(file.bytes,
|
future: () => Matrix.of(context).client.uploadContent(
|
||||||
filename: file.name, contentType: file.mimeType),
|
file.bytes,
|
||||||
|
filename: file.name,
|
||||||
|
contentType: file.mimeType,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
if (uploadResp.error == null) {
|
if (uploadResp.error == null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math' as math;
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
|
|
||||||
import 'package:blurhash_dart/blurhash_dart.dart';
|
import 'package:blurhash_dart/blurhash_dart.dart';
|
||||||
import 'package:image/image.dart';
|
import 'package:image/image.dart';
|
||||||
import 'package:matrix/matrix.dart';
|
import 'package:matrix/matrix.dart';
|
||||||
@ -50,74 +47,15 @@ extension ResizeImage on MatrixFile {
|
|||||||
return MatrixImageFile(
|
return MatrixImageFile(
|
||||||
bytes: bytes,
|
bytes: bytes,
|
||||||
name: name,
|
name: name,
|
||||||
).resizeImage();
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
SentryController.captureException(e, s);
|
SentryController.captureException(e, s);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MatrixImageFile> resizeImage({
|
|
||||||
bool calcBlurhash = true,
|
|
||||||
int max = ResizeImage.max,
|
|
||||||
int quality = ResizeImage.quality,
|
|
||||||
}) async {
|
|
||||||
final bytes = mimeType == 'image/gif'
|
|
||||||
? this.bytes
|
|
||||||
: await compute<_ResizeBytesConfig, Uint8List>(
|
|
||||||
resizeBytes,
|
|
||||||
_ResizeBytesConfig(
|
|
||||||
bytes: this.bytes,
|
|
||||||
mimeType: mimeType,
|
|
||||||
max: max,
|
|
||||||
quality: quality,
|
|
||||||
));
|
|
||||||
final blurhash = calcBlurhash
|
|
||||||
? await compute<Uint8List, BlurHash>(createBlurHash, bytes)
|
|
||||||
: null;
|
|
||||||
return MatrixImageFile(
|
|
||||||
bytes: bytes,
|
|
||||||
name: '${name.split('.').first}_thumbnail_$max.jpg',
|
|
||||||
blurhash: blurhash?.hash,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BlurHash> createBlurHash(Uint8List file) async {
|
Future<BlurHash> createBlurHash(Uint8List file) async {
|
||||||
final image = decodeImage(file)!;
|
final image = decodeImage(file)!;
|
||||||
return BlurHash.encode(image, numCompX: 4, numCompY: 3);
|
return BlurHash.encode(image, numCompX: 4, numCompY: 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List> resizeBytes(_ResizeBytesConfig config) async {
|
|
||||||
var image = decodeImage(config.bytes)!;
|
|
||||||
|
|
||||||
// Is file already smaller than max? Then just return.
|
|
||||||
if (math.max(image.width, image.height) > config.max) {
|
|
||||||
// Use the larger side to resize.
|
|
||||||
final useWidth = image.width >= image.height;
|
|
||||||
image = useWidth
|
|
||||||
? copyResize(image, width: config.max)
|
|
||||||
: copyResize(image, height: config.max);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pngMimeType = 'image/png';
|
|
||||||
final encoded = config.mimeType.toLowerCase() == pngMimeType
|
|
||||||
? encodePng(image)
|
|
||||||
: encodeJpg(image, quality: config.quality);
|
|
||||||
|
|
||||||
return Uint8List.fromList(encoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ResizeBytesConfig {
|
|
||||||
final Uint8List bytes;
|
|
||||||
final int max;
|
|
||||||
final int quality;
|
|
||||||
final String mimeType;
|
|
||||||
|
|
||||||
const _ResizeBytesConfig({
|
|
||||||
required this.bytes,
|
|
||||||
this.max = ResizeImage.max,
|
|
||||||
this.quality = ResizeImage.quality,
|
|
||||||
required this.mimeType,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import 'package:matrix/matrix.dart';
|
|
||||||
|
|
||||||
import 'resize_image.dart';
|
|
||||||
|
|
||||||
extension RoomSendFileExtension on Room {
|
|
||||||
Future<Uri> sendFileEventWithThumbnail(
|
|
||||||
MatrixFile file, {
|
|
||||||
String? txid,
|
|
||||||
Event? inReplyTo,
|
|
||||||
String? editEventId,
|
|
||||||
bool? waitUntilSent,
|
|
||||||
Map<String, dynamic>? extraContent,
|
|
||||||
}) async {
|
|
||||||
MatrixImageFile? thumbnail;
|
|
||||||
if (file is MatrixImageFile) {
|
|
||||||
thumbnail = await file.resizeImage();
|
|
||||||
|
|
||||||
if (thumbnail.size > file.size ~/ 2) {
|
|
||||||
thumbnail = null;
|
|
||||||
}
|
|
||||||
} else if (file is MatrixVideoFile) {
|
|
||||||
thumbnail = await file.getVideoThumbnail();
|
|
||||||
}
|
|
||||||
|
|
||||||
return sendFileEvent(
|
|
||||||
file,
|
|
||||||
txid: txid,
|
|
||||||
inReplyTo: inReplyTo,
|
|
||||||
editEventId: editEventId,
|
|
||||||
waitUntilSent: waitUntilSent ?? false,
|
|
||||||
thumbnail: thumbnail,
|
|
||||||
extraContent: extraContent,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -486,7 +486,7 @@ packages:
|
|||||||
name: flutter_native_splash
|
name: flutter_native_splash
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "2.0.1+1"
|
||||||
flutter_olm:
|
flutter_olm:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -818,7 +818,7 @@ packages:
|
|||||||
name: matrix
|
name: matrix
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.3"
|
version: "0.8.0"
|
||||||
matrix_api_lite:
|
matrix_api_lite:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -50,7 +50,7 @@ dependencies:
|
|||||||
intl: any
|
intl: any
|
||||||
localstorage: ^4.0.0+1
|
localstorage: ^4.0.0+1
|
||||||
lottie: ^1.2.1
|
lottie: ^1.2.1
|
||||||
matrix: ^0.7.3
|
matrix: ^0.8.0
|
||||||
matrix_link_text: ^1.0.2
|
matrix_link_text: ^1.0.2
|
||||||
open_noti_settings: ^0.4.0
|
open_noti_settings: ^0.4.0
|
||||||
package_info_plus: ^1.2.1
|
package_info_plus: ^1.2.1
|
||||||
@ -82,7 +82,7 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
dart_code_metrics: ^4.2.0-dev.3
|
dart_code_metrics: ^4.2.0-dev.3
|
||||||
flutter_lints: ^1.0.4
|
flutter_lints: ^1.0.4
|
||||||
flutter_native_splash: ^1.2.4
|
flutter_native_splash: ^2.0.1+1
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
import_sorter: ^4.6.0
|
import_sorter: ^4.6.0
|
||||||
|
Loading…
Reference in New Issue
Block a user