diff --git a/lib/src/services/api/mediafiles/upload.api.dart b/lib/src/services/api/mediafiles/upload.api.dart index deb7be04..ea4cc518 100644 --- a/lib/src/services/api/mediafiles/upload.api.dart +++ b/lib/src/services/api/mediafiles/upload.api.dart @@ -227,46 +227,54 @@ Future finishStartedPreprocessing() async { if (!service.originalPath.existsSync() && !service.uploadRequestPath.existsSync()) { if (service.storedPath.existsSync()) { - // media files was just stored.. - continue; - } - if (mediaFile.reuploadRequestedBy != null) { - Log.warn( - 'Reupload requested for ${mediaFile.mediaId} but files are missing. Cancelling reupload but keeping record.', - ); - await twonlyDB.mediaFilesDao.updateMedia( - mediaFile.mediaId, - const MediaFilesCompanion( - uploadState: Value(UploadState.uploaded), - reuploadRequestedBy: Value(null), - ), - ); - continue; - } - - final messages = await twonlyDB.messagesDao.getMessagesByMediaId( - mediaFile.mediaId, - ); - - if (messages.isEmpty) { - Log.info( - 'Deleted media files ${mediaFile.mediaId} as originalPath and uploadRequestPath both do not exists and no messages reference it.', - ); - // the file does not exists anymore and no messages reference it. - await twonlyDB.mediaFilesDao.deleteMediaFile(mediaFile.mediaId); + // media file was stored, we can recover tempPath from storedPath and upload it. + try { + if (!service.tempPath.existsSync()) { + service.storedPath.copySync(service.tempPath.path); + } + } catch (e) { + Log.error('Error recovering tempPath from storedPath: $e'); + continue; + } } else { - Log.warn( - 'Media files ${mediaFile.mediaId} missing but messages still reference it. Keeping record to avoid broken chat history.', - ); - // Just mark as uploaded to stop preprocessing attempts - await twonlyDB.mediaFilesDao.updateMedia( + if (mediaFile.reuploadRequestedBy != null) { + Log.warn( + 'Reupload requested for ${mediaFile.mediaId} but files are missing. Cancelling reupload but keeping record.', + ); + await twonlyDB.mediaFilesDao.updateMedia( + mediaFile.mediaId, + const MediaFilesCompanion( + uploadState: Value(UploadState.uploaded), + reuploadRequestedBy: Value(null), + ), + ); + continue; + } + + final messages = await twonlyDB.messagesDao.getMessagesByMediaId( mediaFile.mediaId, - const MediaFilesCompanion( - uploadState: Value(UploadState.uploaded), - ), ); + + if (messages.isEmpty) { + Log.info( + 'Deleted media files ${mediaFile.mediaId} as originalPath and uploadRequestPath both do not exists and no messages reference it.', + ); + // the file does not exists anymore and no messages reference it. + await twonlyDB.mediaFilesDao.deleteMediaFile(mediaFile.mediaId); + } else { + Log.warn( + 'Media files ${mediaFile.mediaId} missing but messages still reference it. Keeping record to avoid broken chat history.', + ); + // Just mark as uploaded to stop preprocessing attempts + await twonlyDB.mediaFilesDao.updateMedia( + mediaFile.mediaId, + const MediaFilesCompanion( + uploadState: Value(UploadState.uploaded), + ), + ); + } + continue; } - continue; } Log.info( 'Finishing started preprocessing of ${mediaFile.mediaId} in state ${mediaFile.uploadState}.', @@ -434,14 +442,38 @@ Future _startBackgroundMediaUploadInternal( if (mediaService.mediaFile.uploadState == UploadState.initialized || mediaService.mediaFile.uploadState == UploadState.preprocessing) { Log.info( - 'Hanlding media file ${mediaService.mediaFile.mediaId} in ${mediaService.mediaFile.uploadState}', + 'Handling media file ${mediaService.mediaFile.mediaId} in ${mediaService.mediaFile.uploadState}', ); await mediaService.setUploadState(UploadState.preprocessing); if (!mediaService.tempPath.existsSync()) { - await mediaService.compressMedia(); + if (mediaService.storedPath.existsSync()) { + try { + mediaService.storedPath.copySync(mediaService.tempPath.path); + } catch (e) { + Log.error('Error copying storedPath to tempPath: $e'); + } + } else { + await mediaService.compressMedia(); + } if (!mediaService.tempPath.existsSync()) { + final messages = await twonlyDB.messagesDao.getMessagesByMediaId( + mediaService.mediaFile.mediaId, + ); + if (messages.isEmpty) { + Log.warn( + 'Media files ${mediaService.mediaFile.mediaId} has no original, temp, or stored path. Removing it from DB as files are not existent.', + ); + await twonlyDB.mediaFilesDao.deleteMediaFile( + mediaService.mediaFile.mediaId, + ); + } else { + Log.warn( + 'Media files ${mediaService.mediaFile.mediaId} has no original, temp, or stored path, but messages still reference it. Marking as uploaded to stop retries.', + ); + await mediaService.setUploadState(UploadState.uploaded); + } return; } } diff --git a/lib/src/visual/views/chats/chat_messages_components/message_input.dart b/lib/src/visual/views/chats/chat_messages_components/message_input.dart index 01c879e3..818f7643 100644 --- a/lib/src/visual/views/chats/chat_messages_components/message_input.dart +++ b/lib/src/visual/views/chats/chat_messages_components/message_input.dart @@ -103,6 +103,19 @@ class _MessageInputState extends State { widget.textFieldFocus.dispose(); recorderController.dispose(); _nextTypingIndicator?.cancel(); + + // Persist draft message on close + final draftText = _textFieldController.text; + unawaited( + twonlyDB.groupsDao.updateGroup( + widget.group.groupId, + GroupsCompanion( + draftMessage: Value(draftText.isEmpty ? null : draftText), + ), + ), + ); + _textFieldController.dispose(); + super.dispose(); } @@ -293,22 +306,15 @@ class _MessageInputState extends State { TextField( controller: _textFieldController, focusNode: widget.textFieldFocus, - textCapitalization: TextCapitalization.sentences, + textCapitalization: + TextCapitalization.sentences, keyboardType: TextInputType.multiline, showCursor: _recordingState != RecordingState.recording, maxLines: 4, minLines: 1, - onChanged: (value) async { + onChanged: (value) { setState(() {}); - await twonlyDB.groupsDao.updateGroup( - widget.group.groupId, - GroupsCompanion( - draftMessage: Value( - _textFieldController.text, - ), - ), - ); }, onSubmitted: (_) { _sendMessage(); diff --git a/lib/src/visual/views/chats/media_viewer.view.dart b/lib/src/visual/views/chats/media_viewer.view.dart index 02c10e4e..f8b1d150 100644 --- a/lib/src/visual/views/chats/media_viewer.view.dart +++ b/lib/src/visual/views/chats/media_viewer.view.dart @@ -106,6 +106,18 @@ class _MediaViewerViewState extends State { _disposeVideoController(); + // Persist draft message on close + final draftText = textMessageController.text; + unawaited( + twonlyDB.groupsDao.updateGroup( + widget.group.groupId, + GroupsCompanion( + draftMessage: Value(draftText.isEmpty ? null : draftText), + ), + ), + ); + textMessageController.dispose(); + super.dispose(); } @@ -834,13 +846,8 @@ class _MediaViewerViewState extends State { autofocus: true, controller: textMessageController, textCapitalization: TextCapitalization.sentences, - onChanged: (value) async { - await twonlyDB.groupsDao.updateGroup( - widget.group.groupId, - GroupsCompanion( - draftMessage: Value(textMessageController.text), - ), - ); + onChanged: (value) { + setState(() {}); }, onEditingComplete: () { setState(() {