[REF] unify file upload logic and pool utils
- merge putMediaToCloud and putFileToPool into putFileToCloud with FileUploadMode for media-safe vs generic uploads Signed-off-by: Texas0295 <kimura@texas0295.top>
This commit is contained in:
		@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -649,7 +649,7 @@ Future<void> 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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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<XFile?> cropImage(
 | 
			
		||||
  BuildContext context, {
 | 
			
		||||
  required XFile image,
 | 
			
		||||
@@ -40,98 +42,46 @@ Future<XFile?> cropImage(
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Completer<SnCloudFile?> 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<SnCloudFile?>();
 | 
			
		||||
  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<SnCloudFile?> putMediaToCloud({
 | 
			
		||||
Completer<SnCloudFile?> 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<SnCloudFile?>();
 | 
			
		||||
 | 
			
		||||
  // 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 = <String, String>{};
 | 
			
		||||
            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<SnCloudFile?> 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<SnCloudFile?> putMediaToCloud({
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If not an image or on web, continue with normal upload
 | 
			
		||||
  _processUpload(
 | 
			
		||||
    fileData,
 | 
			
		||||
    atk,
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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...');
 | 
			
		||||
 
 | 
			
		||||
@@ -247,7 +247,7 @@ class _ShareSheetState extends ConsumerState<ShareSheet> {
 | 
			
		||||
            for (var idx = 0; idx < universalFiles.length; idx++) {
 | 
			
		||||
              final file = universalFiles[idx];
 | 
			
		||||
              final cloudFile =
 | 
			
		||||
                  await putMediaToCloud(
 | 
			
		||||
                  await putFileToCloud(
 | 
			
		||||
                    fileData: file,
 | 
			
		||||
                    atk: token,
 | 
			
		||||
                    baseUrl: serverUrl,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user