diff --git a/lib/src/providers/api/media_send.dart b/lib/src/providers/api/media_send.dart index a5c4753..8576f32 100644 --- a/lib/src/providers/api/media_send.dart +++ b/lib/src/providers/api/media_send.dart @@ -91,11 +91,6 @@ Future retryMediaUpload({int maxRetries = 3}) async { } } }); - if (maxRetries == 0) return; - // retry upload - // Future.delayed(const Duration(milliseconds: 1000), () { - // retryMediaUpload(maxRetries: maxRetries - 1); - // }); } Future initMediaUpload() async { @@ -576,7 +571,7 @@ Future deleteMediaFile(int mediaUploadId, String type) async { } } -Future getMediaFilePath(int mediaId, String type) async { +Future getMediaFilePath(dynamic mediaId, String type) async { final basedir = await getApplicationSupportDirectory(); final mediaSendDir = Directory(join(basedir.path, 'media', type)); if (!await mediaSendDir.exists()) { @@ -585,6 +580,15 @@ Future getMediaFilePath(int mediaId, String type) async { return join(mediaSendDir.path, '$mediaId'); } +Future getMediaBaseFilePath(String type) async { + final basedir = await getApplicationSupportDirectory(); + final mediaSendDir = Directory(join(basedir.path, 'media', type)); + if (!await mediaSendDir.exists()) { + await mediaSendDir.create(recursive: true); + } + return mediaSendDir.path; +} + /// combines two utf8 list Uint8List combineUint8Lists(Uint8List list1, Uint8List list2) { final combinedLength = 4 + list1.length + list2.length; diff --git a/lib/src/views/camera/components/save_to_gallery.dart b/lib/src/views/camera/components/save_to_gallery.dart index d7f9169..875e428 100644 --- a/lib/src/views/camera/components/save_to_gallery.dart +++ b/lib/src/views/camera/components/save_to_gallery.dart @@ -1,6 +1,9 @@ import 'dart:io'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:path/path.dart'; +import 'package:twonly/src/providers/api/media_send.dart'; import 'dart:typed_data'; import 'package:twonly/src/utils/misc.dart'; @@ -9,11 +12,13 @@ class SaveToGalleryButton extends StatefulWidget { final Future Function() getMergedImage; final String? sendNextMediaToUserName; final File? videoFilePath; + final int? mediaUploadId; const SaveToGalleryButton( {super.key, required this.getMergedImage, this.sendNextMediaToUserName, + this.mediaUploadId, this.videoFilePath}); @override @@ -41,12 +46,26 @@ class SaveToGalleryButtonState extends State { }); String? res; + String memoryPath = await getMediaBaseFilePath("memories"); + + if (widget.mediaUploadId != null) { + memoryPath = join(memoryPath, "${widget.mediaUploadId!}"); + } else { + final Random random = Random(); + String token = uint8ListToHex( + List.generate(32, (i) => random.nextInt(256))); + memoryPath = join(memoryPath, token); + } if (widget.videoFilePath != null) { + memoryPath += ".mp4"; + await File(widget.videoFilePath!.path).copy(memoryPath); res = await saveVideoToGallery(widget.videoFilePath!.path); } else { + memoryPath += ".png"; Uint8List? imageBytes = await widget.getMergedImage(); if (imageBytes == null || !context.mounted) return; + await File(memoryPath).writeAsBytes(imageBytes); res = await saveImageToGallery(imageBytes); } if (res == null) { diff --git a/lib/src/views/camera/share_image_editor_view.dart b/lib/src/views/camera/share_image_editor_view.dart index 169e8a0..4990f77 100644 --- a/lib/src/views/camera/share_image_editor_view.dart +++ b/lib/src/views/camera/share_image_editor_view.dart @@ -535,6 +535,7 @@ class _ShareImageEditorView extends State { children: [ SaveToGalleryButton( getMergedImage: getMergedImage, + mediaUploadId: mediaUploadId, videoFilePath: widget.videoFilePath, sendNextMediaToUserName: sendNextMediaToUserName, ), diff --git a/lib/src/views/gallery/gallery_main_view.dart b/lib/src/views/gallery/gallery_main_view.dart index fde4bdd..b5b315c 100644 --- a/lib/src/views/gallery/gallery_main_view.dart +++ b/lib/src/views/gallery/gallery_main_view.dart @@ -21,7 +21,7 @@ class GalleryItem { this.imagePath, this.videoPath, }); - final int id; + final String id; final List messages; final DateTime date; final File? imagePath; @@ -135,6 +135,40 @@ class GalleryMainViewState extends State { initAsync(); } + Future> loadMemoriesDirectory() async { + final directoryPath = await send.getMediaBaseFilePath("memories"); + final directory = Directory(directoryPath); + + List items = []; + if (await directory.exists()) { + final files = directory.listSync(); + + for (var file in files) { + if (file is File) { + final fileName = file.uri.pathSegments.last; + File? imagePath; + File? videoPath; + if (fileName.contains(".png")) { + imagePath = file; + } else if (fileName.contains(".mp4")) { + videoPath = file; + } else { + break; + } + final creationDate = await file.lastModified(); + items.add(GalleryItem( + id: fileName, + messages: [], + date: creationDate, + imagePath: imagePath, + videoPath: videoPath, + )); + } + } + } + return items; + } + Future initAsync() async { List storedMediaFiles = await twonlyDatabase.messagesDao.getAllStoredMediaFiles(); @@ -160,7 +194,7 @@ class GalleryMainViewState extends State { .putIfAbsent( id, () => GalleryItem( - id: id, + id: id.toString(), messages: [], date: message.sendAt, imagePath: imagePath, @@ -174,7 +208,9 @@ class GalleryMainViewState extends State { months = []; String lastMonth = ""; - galleryItems = items.values.toList(); + galleryItems = await loadMemoriesDirectory(); + galleryItems += items.values.toList(); + galleryItems.sort((a, b) => b.date.compareTo(a.date)); for (var i = 0; i < galleryItems.length; i++) { String month = DateFormat('MMMM yyyy').format(galleryItems[i].date); if (lastMonth != month) { @@ -232,9 +268,9 @@ class GalleryMainViewState extends State { MaterialPageRoute( builder: (context) => GalleryPhotoViewWrapper( galleryItems: galleryItems, - backgroundDecoration: const BoxDecoration( - color: Colors.black, - ), + // backgroundDecoration: const BoxDecoration( + // color: Colors.black, + // ), initialIndex: index, scrollDirection: verticalGallery ? Axis.vertical : Axis.horizontal, ), @@ -298,12 +334,10 @@ class _GalleryPhotoViewWrapperState extends State { FilledButton.icon( icon: FaIcon(FontAwesomeIcons.solidPaperPlane), onPressed: () async { - await Navigator.push( + Navigator.push( context, - PageRouteBuilder( - opaque: false, - pageBuilder: (context, a1, a2) => - ShareImageEditorView( + MaterialPageRoute( + builder: (context) => ShareImageEditorView( videoFilePath: widget.galleryItems[currentIndex].videoPath, imageBytes: widget @@ -312,12 +346,6 @@ class _GalleryPhotoViewWrapperState extends State { mirrorVideo: false, useHighQuality: true, ), - transitionsBuilder: - (context, animation, secondaryAnimation, child) { - return child; - }, - transitionDuration: Duration.zero, - reverseTransitionDuration: Duration.zero, ), ); },