mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 06:28:41 +00:00
parent
27483bccd6
commit
910f5f79fa
22 changed files with 244 additions and 64 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit fb66274bf729cde6f7184ec6f7f9ea89f12450fd
|
||||
Subproject commit 83475a912851acb6a718ea32a6f0f754d64a50d8
|
||||
|
|
@ -457,5 +457,6 @@
|
|||
"gotLinkFromFriend": "Ja, der Link kommt direkt von der Person.",
|
||||
"couldNotVerifyUsername": "{username} konnte nicht verifiziert werden",
|
||||
"linkPubkeyDoesNotMatch": "Der öffentliche Schlüssel im Link stimmt nicht mit dem für diesen Kontakt gespeicherten öffentlichen Schlüssel überein. Triff die Person persönlich und scanne den QR-Code direkt!",
|
||||
"startWithCameraOpen": "Mit geöffneter Kamera starten"
|
||||
"startWithCameraOpen": "Mit geöffneter Kamera starten",
|
||||
"showImagePreviewWhenSending": "Bildvorschau bei der Auswahl von Empfängern anzeigen"
|
||||
}
|
||||
|
|
@ -487,5 +487,6 @@
|
|||
"gotLinkFromFriend": "Yes, I got the link from my friend!",
|
||||
"couldNotVerifyUsername": "Could not verify {username}",
|
||||
"linkPubkeyDoesNotMatch": "The public key in the link does not match the public key stored for this contact. Try to meet your friend in person and scan the QR code directly!",
|
||||
"startWithCameraOpen": "Start with camera open"
|
||||
"startWithCameraOpen": "Start with camera open",
|
||||
"showImagePreviewWhenSending": "Display image preview when selecting recipients"
|
||||
}
|
||||
|
|
@ -2845,6 +2845,12 @@ abstract class AppLocalizations {
|
|||
/// In en, this message translates to:
|
||||
/// **'Start with camera open'**
|
||||
String get startWithCameraOpen;
|
||||
|
||||
/// No description provided for @showImagePreviewWhenSending.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Display image preview when selecting recipients'**
|
||||
String get showImagePreviewWhenSending;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
|
|
|
|||
|
|
@ -1573,4 +1573,8 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get startWithCameraOpen => 'Mit geöffneter Kamera starten';
|
||||
|
||||
@override
|
||||
String get showImagePreviewWhenSending =>
|
||||
'Bildvorschau bei der Auswahl von Empfängern anzeigen';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1563,4 +1563,8 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
|
||||
@override
|
||||
String get startWithCameraOpen => 'Start with camera open';
|
||||
|
||||
@override
|
||||
String get showImagePreviewWhenSending =>
|
||||
'Display image preview when selecting recipients';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@ class UserData {
|
|||
@JsonKey(defaultValue: true)
|
||||
bool showFeedbackShortcut = true;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
bool showShowImagePreviewWhenSending = true;
|
||||
|
||||
@JsonKey(defaultValue: true)
|
||||
bool startWithCameraOpen = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
|
|||
..requestedAudioPermission =
|
||||
json['requestedAudioPermission'] as bool? ?? false
|
||||
..showFeedbackShortcut = json['showFeedbackShortcut'] as bool? ?? true
|
||||
..showShowImagePreviewWhenSending =
|
||||
json['showShowImagePreviewWhenSending'] as bool? ?? true
|
||||
..startWithCameraOpen = json['startWithCameraOpen'] as bool? ?? true
|
||||
..preSelectedEmojies = (json['preSelectedEmojies'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
|
|
@ -94,6 +96,8 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
|||
'defaultShowTime': instance.defaultShowTime,
|
||||
'requestedAudioPermission': instance.requestedAudioPermission,
|
||||
'showFeedbackShortcut': instance.showFeedbackShortcut,
|
||||
'showShowImagePreviewWhenSending':
|
||||
instance.showShowImagePreviewWhenSending,
|
||||
'startWithCameraOpen': instance.startWithCameraOpen,
|
||||
'preSelectedEmojies': instance.preSelectedEmojies,
|
||||
'autoDownloadOptions': instance.autoDownloadOptions,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ Future<MediaFileService?> initializeMediaUpload(
|
|||
Future<void> insertMediaFileInMessagesTable(
|
||||
MediaFileService mediaService,
|
||||
List<String> groupIds,
|
||||
Future<Uint8List?>? imageStoreAwait,
|
||||
) async {
|
||||
await twonlyDB.mediaFilesDao.updateAllMediaFiles(
|
||||
const MediaFilesCompanion(
|
||||
|
|
@ -117,6 +118,13 @@ Future<void> insertMediaFileInMessagesTable(
|
|||
}
|
||||
}
|
||||
|
||||
if (imageStoreAwait != null) {
|
||||
if (await imageStoreAwait == null) {
|
||||
Log.error('image store as original did return false...');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unawaited(startBackgroundMediaUpload(mediaService));
|
||||
}
|
||||
|
||||
|
|
|
|||
104
lib/src/utils/screenshot.dart
Normal file
104
lib/src/utils/screenshot.dart
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as io;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
||||
class ScreenshotImage {
|
||||
ScreenshotImage({
|
||||
this.image,
|
||||
this.imageBytes,
|
||||
this.imageBytesFuture,
|
||||
this.file,
|
||||
});
|
||||
|
||||
io.Image? image;
|
||||
Uint8List? imageBytes;
|
||||
Future<Uint8List>? imageBytesFuture;
|
||||
File? file;
|
||||
|
||||
Future<Uint8List?> getBytes() async {
|
||||
if (imageBytes != null) {
|
||||
return imageBytes;
|
||||
}
|
||||
if (imageBytesFuture != null) {
|
||||
return imageBytesFuture;
|
||||
}
|
||||
if (file != null) {
|
||||
return file!.readAsBytes();
|
||||
}
|
||||
if (image == null) return null;
|
||||
final img = await image!.toByteData(format: io.ImageByteFormat.png);
|
||||
if (img == null) {
|
||||
Log.error('Got no image');
|
||||
return null;
|
||||
}
|
||||
return imageBytes = img.buffer.asUint8List();
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenshotController {
|
||||
ScreenshotController() {
|
||||
_containerKey = GlobalKey();
|
||||
}
|
||||
late GlobalKey _containerKey;
|
||||
|
||||
Future<ScreenshotImage?> capture({double? pixelRatio}) async {
|
||||
try {
|
||||
final findRenderObject = _containerKey.currentContext?.findRenderObject();
|
||||
if (findRenderObject == null) {
|
||||
return null;
|
||||
}
|
||||
final boundary = findRenderObject as RenderRepaintBoundary;
|
||||
final context = _containerKey.currentContext;
|
||||
var tmpPixelRatio = pixelRatio;
|
||||
if (tmpPixelRatio == null) {
|
||||
if (context != null && context.mounted) {
|
||||
tmpPixelRatio =
|
||||
tmpPixelRatio ?? MediaQuery.of(context).devicePixelRatio;
|
||||
}
|
||||
}
|
||||
final image = await boundary.toImage(pixelRatio: tmpPixelRatio ?? 1);
|
||||
return ScreenshotImage(image: image);
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Screenshot extends StatefulWidget {
|
||||
const Screenshot({
|
||||
required this.child,
|
||||
required this.controller,
|
||||
super.key,
|
||||
});
|
||||
final Widget? child;
|
||||
final ScreenshotController controller;
|
||||
|
||||
@override
|
||||
State<Screenshot> createState() {
|
||||
return ScreenshotState();
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenshotState extends State<Screenshot> with TickerProviderStateMixin {
|
||||
late ScreenshotController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = widget.controller;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RepaintBoundary(
|
||||
key: _controller._containerKey,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -19,6 +18,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/qr.dart';
|
||||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/permissions_view.dart';
|
||||
|
|
@ -316,7 +316,6 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
|
||||
Future<void> takePicture() async {
|
||||
if (_sharePreviewIsShown || _isVideoRecording) return;
|
||||
late Future<Uint8List?> imageBytes;
|
||||
|
||||
setState(() {
|
||||
_sharePreviewIsShown = true;
|
||||
|
|
@ -345,10 +344,10 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
return;
|
||||
}
|
||||
|
||||
imageBytes = mc.screenshotController
|
||||
final image = await mc.screenshotController
|
||||
.capture(pixelRatio: MediaQuery.of(context).devicePixelRatio);
|
||||
|
||||
if (await pushMediaEditor(imageBytes, null)) {
|
||||
if (await pushMediaEditor(image, null)) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
|
|
@ -357,7 +356,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
}
|
||||
|
||||
Future<bool> pushMediaEditor(
|
||||
Future<Uint8List?>? imageBytes,
|
||||
ScreenshotImage? imageBytes,
|
||||
File? videoFilePath, {
|
||||
bool sharedFromGallery = false,
|
||||
MediaType? mediaType,
|
||||
|
|
@ -478,7 +477,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
Log.info('Picket from gallery: ${pickedFile.path}');
|
||||
|
||||
File? videoFilePath;
|
||||
Future<Uint8List>? imageBytes;
|
||||
ScreenshotImage? image;
|
||||
MediaType? mediaType;
|
||||
|
||||
final isImage =
|
||||
|
|
@ -487,13 +486,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
if (pickedFile.name.contains('.gif')) {
|
||||
mediaType = MediaType.gif;
|
||||
}
|
||||
imageBytes = pickedFile.readAsBytes();
|
||||
image = ScreenshotImage(imageBytesFuture: pickedFile.readAsBytes());
|
||||
} else {
|
||||
videoFilePath = File(pickedFile.path);
|
||||
}
|
||||
|
||||
await pushMediaEditor(
|
||||
imageBytes,
|
||||
image,
|
||||
videoFilePath,
|
||||
sharedFromGallery: true,
|
||||
mediaType: mediaType,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import 'package:drift/drift.dart' show Value;
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/qr.pb.dart';
|
||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/qr.dart';
|
||||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||
import 'package:twonly/src/views/camera/painters/barcode_detector_painter.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
|
|
@ -15,7 +16,7 @@ class SaveToGalleryButton extends StatefulWidget {
|
|||
required this.mediaService,
|
||||
super.key,
|
||||
});
|
||||
final Future<bool> Function() storeImageAsOriginal;
|
||||
final Future<Uint8List?> Function() storeImageAsOriginal;
|
||||
final bool displayButtonLabel;
|
||||
final MediaFileService mediaService;
|
||||
final bool isLoading;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||
import 'package:hand_signature/signature.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:hand_signature/src/utils.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import 'package:drift/drift.dart' show Value;
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:screenshot/screenshot.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||
|
|
@ -15,6 +14,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
||||
|
|
@ -33,14 +33,15 @@ List<Layer> undoLayers = [];
|
|||
List<Layer> removedLayers = [];
|
||||
|
||||
class ShareImageEditorView extends StatefulWidget {
|
||||
const ShareImageEditorView(
|
||||
{required this.sharedFromGallery,
|
||||
required this.mediaFileService,
|
||||
super.key,
|
||||
this.imageBytesFuture,
|
||||
this.sendToGroup,
|
||||
this.mainCameraController});
|
||||
final Future<Uint8List?>? imageBytesFuture;
|
||||
const ShareImageEditorView({
|
||||
required this.sharedFromGallery,
|
||||
required this.mediaFileService,
|
||||
super.key,
|
||||
this.imageBytesFuture,
|
||||
this.sendToGroup,
|
||||
this.mainCameraController,
|
||||
});
|
||||
final ScreenshotImage? imageBytesFuture;
|
||||
final Group? sendToGroup;
|
||||
final bool sharedFromGallery;
|
||||
final MediaFileService mediaFileService;
|
||||
|
|
@ -84,9 +85,11 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
loadImage(widget.imageBytesFuture!);
|
||||
} else {
|
||||
if (widget.mediaFileService.tempPath.existsSync()) {
|
||||
loadImage(widget.mediaFileService.tempPath.readAsBytes());
|
||||
loadImage(ScreenshotImage(file: widget.mediaFileService.tempPath));
|
||||
} else if (widget.mediaFileService.originalPath.existsSync()) {
|
||||
loadImage(widget.mediaFileService.originalPath.readAsBytes());
|
||||
loadImage(
|
||||
ScreenshotImage(file: widget.mediaFileService.originalPath),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -383,11 +386,11 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Uint8List?> getEditedImageBytes() async {
|
||||
Future<ScreenshotImage?> getEditedImageBytes() async {
|
||||
if (layers.length == 1) {
|
||||
if (layers.first is BackgroundLayerData) {
|
||||
final image = (layers.first as BackgroundLayerData).image.bytes;
|
||||
return image;
|
||||
return ScreenshotImage(imageBytes: image);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -412,22 +415,31 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
return image;
|
||||
}
|
||||
|
||||
Future<bool> storeImageAsOriginal() async {
|
||||
Future<Uint8List?> storeImageAsOriginal() async {
|
||||
if (mediaService.overlayImagePath.existsSync()) {
|
||||
mediaService.overlayImagePath.deleteSync();
|
||||
}
|
||||
if (mediaService.tempPath.existsSync()) {
|
||||
mediaService.tempPath.deleteSync();
|
||||
}
|
||||
if (mediaService.originalPath.existsSync()) {
|
||||
mediaService.originalPath.deleteSync();
|
||||
}
|
||||
var bytes = imageBytes;
|
||||
if (media.type == MediaType.gif) {
|
||||
mediaService.originalPath.writeAsBytesSync(imageBytes!.toList());
|
||||
} else {
|
||||
final imageBytes = await getEditedImageBytes();
|
||||
if (imageBytes == null) return false;
|
||||
final image = await getEditedImageBytes();
|
||||
if (image == null) return null;
|
||||
bytes = await image.getBytes();
|
||||
if (bytes == null) {
|
||||
Log.error('imageBytes are empty');
|
||||
return null;
|
||||
}
|
||||
if (media.type == MediaType.image || media.type == MediaType.gif) {
|
||||
mediaService.originalPath.writeAsBytesSync(imageBytes);
|
||||
mediaService.originalPath.writeAsBytesSync(bytes!);
|
||||
} else if (media.type == MediaType.video) {
|
||||
mediaService.overlayImagePath.writeAsBytesSync(imageBytes);
|
||||
mediaService.overlayImagePath.writeAsBytesSync(bytes!);
|
||||
} else {
|
||||
Log.error('MediaType not supported: ${media.type}');
|
||||
}
|
||||
|
|
@ -447,12 +459,11 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
.renameSync(MediaFileService(mediaFile).storedPath.path);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Future<void> loadImage(Future<Uint8List?> imageBytesFuture) async {
|
||||
imageBytes = await imageBytesFuture;
|
||||
|
||||
Future<void> loadImage(ScreenshotImage imageBytesFuture) async {
|
||||
imageBytes = await imageBytesFuture.getBytes();
|
||||
// store this image so it can be used as a draft in case the app is restarted
|
||||
mediaService.originalPath.writeAsBytesSync(imageBytes!.toList());
|
||||
|
||||
|
|
@ -486,18 +497,18 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
sendingOrLoadingImage = true;
|
||||
});
|
||||
|
||||
await storeImageAsOriginal();
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
// Insert media file into the messages database and start uploading process in the background
|
||||
await insertMediaFileInMessagesTable(
|
||||
mediaService,
|
||||
[widget.sendToGroup!.groupId],
|
||||
unawaited(
|
||||
insertMediaFileInMessagesTable(
|
||||
mediaService,
|
||||
[widget.sendToGroup!.groupId],
|
||||
storeImageAsOriginal(),
|
||||
),
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/daos/contacts.dao.dart';
|
||||
import 'package:twonly/src/database/daos/groups.dao.dart';
|
||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
|
|
@ -26,7 +28,7 @@ class ShareImageView extends StatefulWidget {
|
|||
});
|
||||
final HashSet<String> selectedGroupIds;
|
||||
final void Function(String, bool) updateSelectedGroupIds;
|
||||
final Future<bool>? mediaStoreFuture;
|
||||
final Future<Uint8List?>? mediaStoreFuture;
|
||||
final MediaFileService mediaFileService;
|
||||
|
||||
@override
|
||||
|
|
@ -41,6 +43,7 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
|
||||
bool sendingImage = false;
|
||||
bool mediaStoreFutureReady = false;
|
||||
Uint8List? _imageBytes;
|
||||
bool hideArchivedUsers = true;
|
||||
final TextEditingController searchUserName = TextEditingController();
|
||||
late StreamSubscription<List<Group>> allGroupSub;
|
||||
|
|
@ -63,10 +66,9 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
|
||||
Future<void> initAsync() async {
|
||||
if (widget.mediaStoreFuture != null) {
|
||||
await widget.mediaStoreFuture;
|
||||
_imageBytes = await widget.mediaStoreFuture;
|
||||
}
|
||||
mediaStoreFutureReady = true;
|
||||
// unawaited(startBackgroundMediaUpload(widget.mediaFileService));
|
||||
if (!mounted) return;
|
||||
setState(() {});
|
||||
}
|
||||
|
|
@ -235,12 +237,31 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
),
|
||||
),
|
||||
floatingActionButton: SizedBox(
|
||||
height: 120,
|
||||
height: 148,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Row(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (widget.mediaFileService.mediaFile.type == MediaType.image &&
|
||||
_imageBytes != null &&
|
||||
gUser.showShowImagePreviewWhenSending)
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
border:
|
||||
Border.all(color: context.color.primary, width: 3),
|
||||
color: context.color.primary,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(7),
|
||||
child: Image.memory(_imageBytes!),
|
||||
),
|
||||
),
|
||||
),
|
||||
FilledButton.icon(
|
||||
icon: !mediaStoreFutureReady || sendingImage
|
||||
? SizedBox(
|
||||
|
|
@ -265,6 +286,7 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
await insertMediaFileInMessagesTable(
|
||||
widget.mediaFileService,
|
||||
widget.selectedGroupIds.toList(),
|
||||
null,
|
||||
);
|
||||
|
||||
if (context.mounted) {
|
||||
|
|
@ -288,7 +310,7 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
),
|
||||
),
|
||||
label: Text(
|
||||
context.lang.shareImagedEditorSendImage,
|
||||
'${context.lang.shareImagedEditorSendImage} (${widget.selectedGroupIds.length})',
|
||||
style: const TextStyle(fontSize: 17),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ class _MessageInputState extends State<MessageInput> {
|
|||
await insertMediaFileInMessagesTable(
|
||||
mediaFileService,
|
||||
[widget.group.groupId],
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,8 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
|||
);
|
||||
};
|
||||
}
|
||||
if (mediaFile.uploadState == UploadState.preprocessing) {
|
||||
if (mediaFile.uploadState == UploadState.preprocessing ||
|
||||
mediaFile.uploadState == UploadState.initialized) {
|
||||
text = context.lang.inProcess;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,16 @@ class _AppearanceViewState extends State<AppearanceView> {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> toggleShowImagePreviewWhenSending() async {
|
||||
await updateUserdata((u) {
|
||||
u.showShowImagePreviewWhenSending = !u.showShowImagePreviewWhenSending;
|
||||
return u;
|
||||
});
|
||||
setState(() {
|
||||
// gUser
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedTheme = context.watch<SettingsChangeProvider>().themeMode;
|
||||
|
|
@ -127,6 +137,14 @@ class _AppearanceViewState extends State<AppearanceView> {
|
|||
onChanged: (a) => toggleStartWithCameraOpen(),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(context.lang.showImagePreviewWhenSending),
|
||||
onTap: toggleShowImagePreviewWhenSending,
|
||||
trailing: Switch(
|
||||
value: gUser.showShowImagePreviewWhenSending,
|
||||
onChanged: (a) => toggleShowImagePreviewWhenSending(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
15
pubspec.lock
15
pubspec.lock
|
|
@ -882,10 +882,9 @@ packages:
|
|||
hand_signature:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hand_signature
|
||||
sha256: "05b40d3b2d1885a5dda126f26db386660aa46e497b63c96feb91d3198a667eea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: "dependencies/hand_signature"
|
||||
relative: true
|
||||
source: path
|
||||
version: "3.1.0+2"
|
||||
hashlib:
|
||||
dependency: "direct main"
|
||||
|
|
@ -1532,14 +1531,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.28.0"
|
||||
screenshot:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: screenshot
|
||||
sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
scrollable_positioned_list:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -78,13 +78,11 @@ dependencies:
|
|||
gal: ^2.3.1
|
||||
get: ^4.7.2
|
||||
google_mlkit_barcode_scanning: ^0.14.1
|
||||
hand_signature: ^3.0.3
|
||||
image: ^4.3.0
|
||||
no_screenshot: ^0.3.1
|
||||
permission_handler: ^12.0.0+1
|
||||
provider: ^6.1.2
|
||||
restart_app: ^1.3.2
|
||||
screenshot: ^3.0.0
|
||||
sentry_flutter: ^9.8.0
|
||||
app_links: ^7.0.0
|
||||
in_app_purchase: ^3.2.3
|
||||
|
|
@ -101,6 +99,7 @@ dependencies:
|
|||
mutex: ^3.1.0
|
||||
introduction_screen: ^4.0.0
|
||||
qr_flutter: ^4.1.0
|
||||
hand_signature: ^3.0.3
|
||||
|
||||
dependency_overrides:
|
||||
dots_indicator:
|
||||
|
|
@ -123,6 +122,8 @@ dependency_overrides:
|
|||
path: ./dependencies/adaptive_number
|
||||
ed25519_edwards:
|
||||
path: ./dependencies/ed25519_edwards
|
||||
hand_signature:
|
||||
path: ./dependencies/hand_signature
|
||||
hashlib_codecs:
|
||||
path: ./dependencies/hashlib_codecs
|
||||
optional:
|
||||
|
|
|
|||
Loading…
Reference in a new issue