Merge pull request #359 from twonlyapp/dev
Some checks failed
Publish on Github / build_and_publish (push) Has been cancelled

- Fixes the issue where black/blank images were sometimes received
- Fixes an issue in the image editor
This commit is contained in:
Tobi 2025-12-28 15:49:31 +01:00 committed by GitHub
commit 6a104e9468
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 58 additions and 29 deletions

View file

@ -1,5 +1,10 @@
# Changelog # Changelog
## 0.0.81
- Fixes the issue where black/blank images were sometimes received
- Fixes an issue in the image editor
## 0.0.80 ## 0.0.80
- Share images/videos directly from other applications - Share images/videos directly from other applications

View file

@ -87,7 +87,6 @@ Future<MediaFileService?> initializeMediaUpload(
Future<void> insertMediaFileInMessagesTable( Future<void> insertMediaFileInMessagesTable(
MediaFileService mediaService, MediaFileService mediaService,
List<String> groupIds, List<String> groupIds,
Future<Uint8List?>? imageStoreAwait,
) async { ) async {
await twonlyDB.mediaFilesDao.updateAllMediaFiles( await twonlyDB.mediaFilesDao.updateAllMediaFiles(
const MediaFilesCompanion( const MediaFilesCompanion(
@ -118,13 +117,6 @@ Future<void> insertMediaFileInMessagesTable(
} }
} }
if (imageStoreAwait != null) {
if (await imageStoreAwait == null) {
Log.error('image store as original did return false...');
return;
}
}
unawaited(startBackgroundMediaUpload(mediaService)); unawaited(startBackgroundMediaUpload(mediaService));
} }

View file

@ -35,6 +35,7 @@ class BackgroundLayerData extends Layer {
required this.image, required this.image,
}); });
ImageItem image; ImageItem image;
bool imageLoaded = false;
} }
class FilterLayerData extends Layer { class FilterLayerData extends Layer {

View file

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/src/views/camera/image_editor/data/layer.dart'; import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
/// Main layer
class BackgroundLayer extends StatefulWidget { class BackgroundLayer extends StatefulWidget {
const BackgroundLayer({ const BackgroundLayer({
required this.layerData, required this.layerData,
@ -23,7 +22,17 @@ class _BackgroundLayerState extends State<BackgroundLayer> {
height: widget.layerData.image.height.toDouble(), height: widget.layerData.image.height.toDouble(),
// color: Theme.of(context).colorScheme.surface, // color: Theme.of(context).colorScheme.surface,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: Image.memory(widget.layerData.image.bytes), child: Image.memory(
widget.layerData.image.bytes,
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
if (wasSynchronouslyLoaded || frame != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.layerData.imageLoaded = true;
});
}
return child;
},
),
); );
} }
} }

View file

@ -64,6 +64,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
VideoPlayerController? videoController; VideoPlayerController? videoController;
ImageItem currentImage = ImageItem(); ImageItem currentImage = ImageItem();
ScreenshotController screenshotController = ScreenshotController(); ScreenshotController screenshotController = ScreenshotController();
Timer? _imageLoadingTimer;
MediaFileService get mediaService => widget.mediaFileService; MediaFileService get mediaService => widget.mediaFileService;
MediaFile get media => widget.mediaFileService.mediaFile; MediaFile get media => widget.mediaFileService.mediaFile;
@ -120,6 +121,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
isDraftMedia: Value(false), isDraftMedia: Value(false),
), ),
); );
_imageLoadingTimer?.cancel();
super.dispose(); super.dispose();
} }
@ -449,6 +451,10 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
x.showCustomButtons = false; x.showCustomButtons = false;
} }
setState(() {}); setState(() {});
// Make a short delay, so the setState does have its effect...
await Future.delayed(const Duration(milliseconds: 10));
final image = await screenshotController.capture( final image = await screenshotController.capture(
pixelRatio: pixelRatio, pixelRatio: pixelRatio,
); );
@ -531,16 +537,34 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
} }
}); });
layers.insert(
0,
BackgroundLayerData(
key: GlobalKey(),
image: currentImage,
),
);
setState(() { setState(() {
sendingOrLoadingImage = false; layers.insert(
loadingImage = false; 0,
BackgroundLayerData(
key: GlobalKey(),
image: currentImage,
),
);
});
// It is important that the user can sending the image only when the image is fully loaded otherwise if the user
// will click on send before the image is painted the screenshot will be transparent..
_imageLoadingTimer =
Timer.periodic(const Duration(milliseconds: 10), (timer) {
final imageLayer = layers.first;
if (imageLayer is BackgroundLayerData) {
if (imageLayer.imageLoaded) {
timer.cancel();
Future.delayed(const Duration(milliseconds: 50), () {
Log.info(imageLayer.imageLoaded);
if (context.mounted) {
setState(() {
sendingOrLoadingImage = false;
loadingImage = false;
});
}
});
}
}
}); });
} }
@ -552,16 +576,16 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
if (!context.mounted) return; if (!context.mounted) return;
// must be awaited so the widget for the screenshot is not already disposed when sending..
await storeImageAsOriginal();
// Insert media file into the messages database and start uploading process in the background // Insert media file into the messages database and start uploading process in the background
unawaited( await insertMediaFileInMessagesTable(
insertMediaFileInMessagesTable( mediaService,
mediaService, [widget.sendToGroup!.groupId],
[widget.sendToGroup!.groupId],
storeImageAsOriginal(),
),
); );
if (context.mounted) { if (mounted) {
Navigator.pop(context, true); Navigator.pop(context, true);
} }
} }

View file

@ -286,7 +286,6 @@ class _ShareImageView extends State<ShareImageView> {
await insertMediaFileInMessagesTable( await insertMediaFileInMessagesTable(
widget.mediaFileService, widget.mediaFileService,
widget.selectedGroupIds.toList(), widget.selectedGroupIds.toList(),
null,
); );
if (context.mounted) { if (context.mounted) {

View file

@ -151,7 +151,6 @@ class _MessageInputState extends State<MessageInput> {
await insertMediaFileInMessagesTable( await insertMediaFileInMessagesTable(
mediaFileService, mediaFileService,
[widget.group.groupId], [widget.group.groupId],
null,
); );
} }

View file

@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
publish_to: 'none' publish_to: 'none'
version: 0.0.80+80 version: 0.0.81+81
environment: environment:
sdk: ^3.6.0 sdk: ^3.6.0