diff --git a/lib/screens/account/me/profile_update.dart b/lib/screens/account/me/profile_update.dart index 41cd808a..6a7241ad 100644 --- a/lib/screens/account/me/profile_update.dart +++ b/lib/screens/account/me/profile_update.dart @@ -66,7 +66,7 @@ class UpdateProfileScreen extends HookConsumerWidget { final token = await getToken(ref.watch(tokenProvider)); if (token == null) throw ArgumentError('Token is null'); final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: UniversalFile( data: result, type: UniversalFileType.image, diff --git a/lib/screens/chat/chat.dart b/lib/screens/chat/chat.dart index 6c08093e..64175afa 100644 --- a/lib/screens/chat/chat.dart +++ b/lib/screens/chat/chat.dart @@ -543,7 +543,7 @@ class EditChatScreen extends HookConsumerWidget { final token = await getToken(ref.watch(tokenProvider)); if (token == null) throw ArgumentError('Token is null'); final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: UniversalFile( data: result, type: UniversalFileType.image, diff --git a/lib/screens/chat/room.dart b/lib/screens/chat/room.dart index 7332c910..4eba2c06 100644 --- a/lib/screens/chat/room.dart +++ b/lib/screens/chat/room.dart @@ -649,7 +649,7 @@ Future loadMore() async { var cloudAttachments = List.empty(growable: true); for (var idx = 0; idx < attachments.length; idx++) { final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: attachments[idx], atk: token, baseUrl: baseUrl, diff --git a/lib/screens/creators/publishers.dart b/lib/screens/creators/publishers.dart index 6a329616..cc96720e 100644 --- a/lib/screens/creators/publishers.dart +++ b/lib/screens/creators/publishers.dart @@ -98,7 +98,7 @@ class EditPublisherScreen extends HookConsumerWidget { final token = await getToken(ref.watch(tokenProvider)); if (token == null) throw ArgumentError('Token is null'); final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: UniversalFile( data: result, type: UniversalFileType.image, diff --git a/lib/screens/developers/edit_app.dart b/lib/screens/developers/edit_app.dart index e4464e0d..fda47fae 100644 --- a/lib/screens/developers/edit_app.dart +++ b/lib/screens/developers/edit_app.dart @@ -141,7 +141,7 @@ class EditAppScreen extends HookConsumerWidget { final token = await getToken(ref.watch(tokenProvider)); if (token == null) throw ArgumentError('Token is null'); final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: UniversalFile( data: result, type: UniversalFileType.image, diff --git a/lib/screens/developers/edit_bot.dart b/lib/screens/developers/edit_bot.dart index fb0b8fcc..3350fafb 100644 --- a/lib/screens/developers/edit_bot.dart +++ b/lib/screens/developers/edit_bot.dart @@ -127,7 +127,7 @@ class EditBotScreen extends HookConsumerWidget { final token = await getToken(ref.watch(tokenProvider)); if (token == null) throw ArgumentError('Token is null'); final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: UniversalFile( data: result, type: UniversalFileType.image, diff --git a/lib/screens/realm/realms.dart b/lib/screens/realm/realms.dart index 01a8b483..d421e593 100644 --- a/lib/screens/realm/realms.dart +++ b/lib/screens/realm/realms.dart @@ -211,7 +211,7 @@ class EditRealmScreen extends HookConsumerWidget { final token = await getToken(ref.watch(tokenProvider)); if (token == null) throw ArgumentError('Access token is null'); final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: UniversalFile( data: result, type: UniversalFileType.image, diff --git a/lib/services/file.dart b/lib/services/file.dart index a2ac2a26..02b3ff30 100644 --- a/lib/services/file.dart +++ b/lib/services/file.dart @@ -11,6 +11,8 @@ import 'package:island/services/file_uploader.dart'; import 'package:native_exif/native_exif.dart'; import 'package:path_provider/path_provider.dart'; +enum FileUploadMode { generic, mediaSafe } + Future cropImage( BuildContext context, { required XFile image, @@ -40,98 +42,46 @@ Future cropImage( ); } -Completer putFileToPool({ - required UniversalFile fileData, - required String atk, - required String baseUrl, - required String poolId, - String? filename, - String? mimetype, - Function(double progress, Duration estimate)? onProgress, -}) { - final completer = Completer(); - final data = fileData.data; - if (data is! XFile) { - completer.completeError( - ArgumentError('Unsupported fileData type for putFileToPool'), - ); - return completer; - } - - final actualFilename = filename ?? data.name; - final actualMimetype = mimetype ?? data.mimeType ?? 'application/octet-stream'; - - final dio = Dio(BaseOptions( - baseUrl: baseUrl, - headers: { - 'Authorization': 'AtField $atk', - 'Accept': 'application/json', - }, - )); - - final uploader = FileUploader(dio); - final fileObj = File(data.path); - - onProgress?.call(0.0, Duration.zero); - uploader.uploadFile( - file: fileObj, - fileName: actualFilename, - contentType: actualMimetype, - poolId: poolId, - ).then((result) { - onProgress?.call(1.0, Duration.zero); - completer.complete(result); - }).catchError((e) { - completer.completeError(e); - }); - - return completer; -} - - -Completer putMediaToCloud({ +Completer putFileToCloud({ required UniversalFile fileData, required String atk, required String baseUrl, String? poolId, String? filename, String? mimetype, + FileUploadMode? mode, Function(double progress, Duration estimate)? onProgress, }) { final completer = Completer(); - // Process the image to remove GPS EXIF data if needed - if (fileData.isOnDevice && fileData.type == UniversalFileType.image) { + final effectiveMode = + mode ?? + (fileData.type == UniversalFileType.file + ? FileUploadMode.generic + : FileUploadMode.mediaSafe); + + if (effectiveMode == FileUploadMode.mediaSafe && + fileData.isOnDevice && + fileData.type == UniversalFileType.image) { final data = fileData.data; if (data is XFile && !kIsWeb && (Platform.isIOS || Platform.isAndroid)) { - // Use native_exif to selectively remove GPS data Exif.fromPath(data.path) - .then((exif) { - // Remove GPS-related attributes - final gpsAttributes = [ - 'GPSLatitude', - 'GPSLatitudeRef', - 'GPSLongitude', - 'GPSLongitudeRef', - 'GPSAltitude', - 'GPSAltitudeRef', - 'GPSTimeStamp', - 'GPSProcessingMethod', - 'GPSDateStamp', - ]; - - // Create a map of attributes to clear - final clearAttributes = {}; - for (final attr in gpsAttributes) { - clearAttributes[attr] = ''; - } - - // Write empty values to remove GPS data - return exif.writeAttributes(clearAttributes); + .then((exif) async { + final gpsAttributes = { + 'GPSLatitude': '', + 'GPSLatitudeRef': '', + 'GPSLongitude': '', + 'GPSLongitudeRef': '', + 'GPSAltitude': '', + 'GPSAltitudeRef': '', + 'GPSTimeStamp': '', + 'GPSProcessingMethod': '', + 'GPSDateStamp': '', + }; + await exif.writeAttributes(gpsAttributes); }) - .then((_) { - // Continue with upload after GPS data is removed - _processUpload( + .then( + (_) => _processUpload( fileData, atk, baseUrl, @@ -140,12 +90,11 @@ Completer putMediaToCloud({ mimetype, onProgress, completer, - ); - }) + ), + ) .catchError((e) { - // If there's an error, continue with the original file debugPrint('Error removing GPS EXIF data: $e'); - _processUpload( + return _processUpload( fileData, atk, baseUrl, @@ -161,7 +110,6 @@ Completer putMediaToCloud({ } } - // If not an image or on web, continue with normal upload _processUpload( fileData, atk, diff --git a/lib/widgets/content/cloud_file_picker.dart b/lib/widgets/content/cloud_file_picker.dart index 3cc8c685..0101855b 100644 --- a/lib/widgets/content/cloud_file_picker.dart +++ b/lib/widgets/content/cloud_file_picker.dart @@ -55,7 +55,7 @@ class CloudFilePicker extends HookConsumerWidget { uploadPosition.value = idx; final file = files.value[idx]; final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: file, atk: token, baseUrl: baseUrl, diff --git a/lib/widgets/post/compose_shared.dart b/lib/widgets/post/compose_shared.dart index c9302376..15de2b4f 100644 --- a/lib/widgets/post/compose_shared.dart +++ b/lib/widgets/post/compose_shared.dart @@ -185,7 +185,7 @@ class ComposeLogic { if (attachment.data is! SnCloudFile) { try { final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: attachment, atk: token, baseUrl: baseUrl, @@ -523,44 +523,34 @@ class ComposeLogic { SnCloudFile? cloudFile; - final pools = await ref.read(poolsProvider.future); - final selectedPoolId = resolveDefaultPoolId(ref, pools); - if (attachment.type == UniversalFileType.file) { - cloudFile = - await putFileToPool( - fileData: attachment, - atk: token, - baseUrl: baseUrl, - poolId: selectedPoolId, - filename: attachment.data.name ?? 'General file', - mimetype: - attachment.data.mimeType ?? - getMimeTypeFromFileType(attachment.type), - onProgress: (progress, _) { - state.attachmentProgress.value = { - ...state.attachmentProgress.value, - index: progress, - }; - }, - ).future; - } else { - cloudFile = - await putMediaToCloud( - fileData: attachment, - atk: token, - baseUrl: baseUrl, - filename: attachment.data.name ?? 'Post media', - mimetype: - attachment.data.mimeType ?? - getMimeTypeFromFileType(attachment.type), - onProgress: (progress, _) { - state.attachmentProgress.value = { - ...state.attachmentProgress.value, - index: progress, - }; - }, - ).future; - } + final pools = await ref.read(poolsProvider.future); + final selectedPoolId = resolveDefaultPoolId(ref, pools); + + cloudFile = + await putFileToCloud( + fileData: attachment, + atk: token, + baseUrl: baseUrl, + poolId: selectedPoolId, + filename: + attachment.data.name ?? + (attachment.type == UniversalFileType.file + ? 'General file' + : 'Post media'), + mimetype: + attachment.data.mimeType ?? + getMimeTypeFromFileType(attachment.type), + mode: + attachment.type == UniversalFileType.file + ? FileUploadMode.generic + : FileUploadMode.mediaSafe, + onProgress: (progress, _) { + state.attachmentProgress.value = { + ...state.attachmentProgress.value, + index: progress, + }; + }, + ).future; if (cloudFile == null) { throw ArgumentError('Failed to upload the file...'); diff --git a/lib/widgets/share/share_sheet.dart b/lib/widgets/share/share_sheet.dart index 037f8203..96b6be82 100644 --- a/lib/widgets/share/share_sheet.dart +++ b/lib/widgets/share/share_sheet.dart @@ -247,7 +247,7 @@ class _ShareSheetState extends ConsumerState { for (var idx = 0; idx < universalFiles.length; idx++) { final file = universalFiles[idx]; final cloudFile = - await putMediaToCloud( + await putFileToCloud( fileData: file, atk: token, baseUrl: serverUrl,