[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));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
if (token == null) throw ArgumentError('Token is null');
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: UniversalFile(
|
fileData: UniversalFile(
|
||||||
data: result,
|
data: result,
|
||||||
type: UniversalFileType.image,
|
type: UniversalFileType.image,
|
||||||
|
@@ -543,7 +543,7 @@ class EditChatScreen extends HookConsumerWidget {
|
|||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
if (token == null) throw ArgumentError('Token is null');
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: UniversalFile(
|
fileData: UniversalFile(
|
||||||
data: result,
|
data: result,
|
||||||
type: UniversalFileType.image,
|
type: UniversalFileType.image,
|
||||||
|
@@ -649,7 +649,7 @@ Future<void> loadMore() async {
|
|||||||
var cloudAttachments = List.empty(growable: true);
|
var cloudAttachments = List.empty(growable: true);
|
||||||
for (var idx = 0; idx < attachments.length; idx++) {
|
for (var idx = 0; idx < attachments.length; idx++) {
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: attachments[idx],
|
fileData: attachments[idx],
|
||||||
atk: token,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
|
@@ -98,7 +98,7 @@ class EditPublisherScreen extends HookConsumerWidget {
|
|||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
if (token == null) throw ArgumentError('Token is null');
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: UniversalFile(
|
fileData: UniversalFile(
|
||||||
data: result,
|
data: result,
|
||||||
type: UniversalFileType.image,
|
type: UniversalFileType.image,
|
||||||
|
@@ -141,7 +141,7 @@ class EditAppScreen extends HookConsumerWidget {
|
|||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
if (token == null) throw ArgumentError('Token is null');
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: UniversalFile(
|
fileData: UniversalFile(
|
||||||
data: result,
|
data: result,
|
||||||
type: UniversalFileType.image,
|
type: UniversalFileType.image,
|
||||||
|
@@ -127,7 +127,7 @@ class EditBotScreen extends HookConsumerWidget {
|
|||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
if (token == null) throw ArgumentError('Token is null');
|
if (token == null) throw ArgumentError('Token is null');
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: UniversalFile(
|
fileData: UniversalFile(
|
||||||
data: result,
|
data: result,
|
||||||
type: UniversalFileType.image,
|
type: UniversalFileType.image,
|
||||||
|
@@ -211,7 +211,7 @@ class EditRealmScreen extends HookConsumerWidget {
|
|||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
if (token == null) throw ArgumentError('Access token is null');
|
if (token == null) throw ArgumentError('Access token is null');
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: UniversalFile(
|
fileData: UniversalFile(
|
||||||
data: result,
|
data: result,
|
||||||
type: UniversalFileType.image,
|
type: UniversalFileType.image,
|
||||||
|
@@ -11,6 +11,8 @@ import 'package:island/services/file_uploader.dart';
|
|||||||
import 'package:native_exif/native_exif.dart';
|
import 'package:native_exif/native_exif.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
enum FileUploadMode { generic, mediaSafe }
|
||||||
|
|
||||||
Future<XFile?> cropImage(
|
Future<XFile?> cropImage(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required XFile image,
|
required XFile image,
|
||||||
@@ -40,98 +42,46 @@ Future<XFile?> cropImage(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Completer<SnCloudFile?> putFileToPool({
|
Completer<SnCloudFile?> putFileToCloud({
|
||||||
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({
|
|
||||||
required UniversalFile fileData,
|
required UniversalFile fileData,
|
||||||
required String atk,
|
required String atk,
|
||||||
required String baseUrl,
|
required String baseUrl,
|
||||||
String? poolId,
|
String? poolId,
|
||||||
String? filename,
|
String? filename,
|
||||||
String? mimetype,
|
String? mimetype,
|
||||||
|
FileUploadMode? mode,
|
||||||
Function(double progress, Duration estimate)? onProgress,
|
Function(double progress, Duration estimate)? onProgress,
|
||||||
}) {
|
}) {
|
||||||
final completer = Completer<SnCloudFile?>();
|
final completer = Completer<SnCloudFile?>();
|
||||||
|
|
||||||
// Process the image to remove GPS EXIF data if needed
|
final effectiveMode =
|
||||||
if (fileData.isOnDevice && fileData.type == UniversalFileType.image) {
|
mode ??
|
||||||
|
(fileData.type == UniversalFileType.file
|
||||||
|
? FileUploadMode.generic
|
||||||
|
: FileUploadMode.mediaSafe);
|
||||||
|
|
||||||
|
if (effectiveMode == FileUploadMode.mediaSafe &&
|
||||||
|
fileData.isOnDevice &&
|
||||||
|
fileData.type == UniversalFileType.image) {
|
||||||
final data = fileData.data;
|
final data = fileData.data;
|
||||||
if (data is XFile && !kIsWeb && (Platform.isIOS || Platform.isAndroid)) {
|
if (data is XFile && !kIsWeb && (Platform.isIOS || Platform.isAndroid)) {
|
||||||
// Use native_exif to selectively remove GPS data
|
|
||||||
Exif.fromPath(data.path)
|
Exif.fromPath(data.path)
|
||||||
.then((exif) {
|
.then((exif) async {
|
||||||
// Remove GPS-related attributes
|
final gpsAttributes = {
|
||||||
final gpsAttributes = [
|
'GPSLatitude': '',
|
||||||
'GPSLatitude',
|
'GPSLatitudeRef': '',
|
||||||
'GPSLatitudeRef',
|
'GPSLongitude': '',
|
||||||
'GPSLongitude',
|
'GPSLongitudeRef': '',
|
||||||
'GPSLongitudeRef',
|
'GPSAltitude': '',
|
||||||
'GPSAltitude',
|
'GPSAltitudeRef': '',
|
||||||
'GPSAltitudeRef',
|
'GPSTimeStamp': '',
|
||||||
'GPSTimeStamp',
|
'GPSProcessingMethod': '',
|
||||||
'GPSProcessingMethod',
|
'GPSDateStamp': '',
|
||||||
'GPSDateStamp',
|
};
|
||||||
];
|
await exif.writeAttributes(gpsAttributes);
|
||||||
|
|
||||||
// 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((_) {
|
.then(
|
||||||
// Continue with upload after GPS data is removed
|
(_) => _processUpload(
|
||||||
_processUpload(
|
|
||||||
fileData,
|
fileData,
|
||||||
atk,
|
atk,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -140,12 +90,11 @@ Completer<SnCloudFile?> putMediaToCloud({
|
|||||||
mimetype,
|
mimetype,
|
||||||
onProgress,
|
onProgress,
|
||||||
completer,
|
completer,
|
||||||
);
|
),
|
||||||
})
|
)
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
// If there's an error, continue with the original file
|
|
||||||
debugPrint('Error removing GPS EXIF data: $e');
|
debugPrint('Error removing GPS EXIF data: $e');
|
||||||
_processUpload(
|
return _processUpload(
|
||||||
fileData,
|
fileData,
|
||||||
atk,
|
atk,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
@@ -161,7 +110,6 @@ Completer<SnCloudFile?> putMediaToCloud({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not an image or on web, continue with normal upload
|
|
||||||
_processUpload(
|
_processUpload(
|
||||||
fileData,
|
fileData,
|
||||||
atk,
|
atk,
|
||||||
|
@@ -55,7 +55,7 @@ class CloudFilePicker extends HookConsumerWidget {
|
|||||||
uploadPosition.value = idx;
|
uploadPosition.value = idx;
|
||||||
final file = files.value[idx];
|
final file = files.value[idx];
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: file,
|
fileData: file,
|
||||||
atk: token,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
|
@@ -185,7 +185,7 @@ class ComposeLogic {
|
|||||||
if (attachment.data is! SnCloudFile) {
|
if (attachment.data is! SnCloudFile) {
|
||||||
try {
|
try {
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: attachment,
|
fileData: attachment,
|
||||||
atk: token,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
@@ -523,44 +523,34 @@ class ComposeLogic {
|
|||||||
|
|
||||||
SnCloudFile? cloudFile;
|
SnCloudFile? cloudFile;
|
||||||
|
|
||||||
final pools = await ref.read(poolsProvider.future);
|
final pools = await ref.read(poolsProvider.future);
|
||||||
final selectedPoolId = resolveDefaultPoolId(ref, pools);
|
final selectedPoolId = resolveDefaultPoolId(ref, pools);
|
||||||
if (attachment.type == UniversalFileType.file) {
|
|
||||||
cloudFile =
|
cloudFile =
|
||||||
await putFileToPool(
|
await putFileToCloud(
|
||||||
fileData: attachment,
|
fileData: attachment,
|
||||||
atk: token,
|
atk: token,
|
||||||
baseUrl: baseUrl,
|
baseUrl: baseUrl,
|
||||||
poolId: selectedPoolId,
|
poolId: selectedPoolId,
|
||||||
filename: attachment.data.name ?? 'General file',
|
filename:
|
||||||
mimetype:
|
attachment.data.name ??
|
||||||
attachment.data.mimeType ??
|
(attachment.type == UniversalFileType.file
|
||||||
getMimeTypeFromFileType(attachment.type),
|
? 'General file'
|
||||||
onProgress: (progress, _) {
|
: 'Post media'),
|
||||||
state.attachmentProgress.value = {
|
mimetype:
|
||||||
...state.attachmentProgress.value,
|
attachment.data.mimeType ??
|
||||||
index: progress,
|
getMimeTypeFromFileType(attachment.type),
|
||||||
};
|
mode:
|
||||||
},
|
attachment.type == UniversalFileType.file
|
||||||
).future;
|
? FileUploadMode.generic
|
||||||
} else {
|
: FileUploadMode.mediaSafe,
|
||||||
cloudFile =
|
onProgress: (progress, _) {
|
||||||
await putMediaToCloud(
|
state.attachmentProgress.value = {
|
||||||
fileData: attachment,
|
...state.attachmentProgress.value,
|
||||||
atk: token,
|
index: progress,
|
||||||
baseUrl: baseUrl,
|
};
|
||||||
filename: attachment.data.name ?? 'Post media',
|
},
|
||||||
mimetype:
|
).future;
|
||||||
attachment.data.mimeType ??
|
|
||||||
getMimeTypeFromFileType(attachment.type),
|
|
||||||
onProgress: (progress, _) {
|
|
||||||
state.attachmentProgress.value = {
|
|
||||||
...state.attachmentProgress.value,
|
|
||||||
index: progress,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
).future;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cloudFile == null) {
|
if (cloudFile == null) {
|
||||||
throw ArgumentError('Failed to upload the file...');
|
throw ArgumentError('Failed to upload the file...');
|
||||||
|
@@ -247,7 +247,7 @@ class _ShareSheetState extends ConsumerState<ShareSheet> {
|
|||||||
for (var idx = 0; idx < universalFiles.length; idx++) {
|
for (var idx = 0; idx < universalFiles.length; idx++) {
|
||||||
final file = universalFiles[idx];
|
final file = universalFiles[idx];
|
||||||
final cloudFile =
|
final cloudFile =
|
||||||
await putMediaToCloud(
|
await putFileToCloud(
|
||||||
fileData: file,
|
fileData: file,
|
||||||
atk: token,
|
atk: token,
|
||||||
baseUrl: serverUrl,
|
baseUrl: serverUrl,
|
||||||
|
Reference in New Issue
Block a user