♻️ Better file upload

This commit is contained in:
2025-10-02 01:13:41 +08:00
parent 3bfc0b8181
commit 8fe3a664a6
23 changed files with 293 additions and 383 deletions

View File

@@ -10,6 +10,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/file.dart';
import 'package:island/services/file_uploader.dart';
import 'package:island/utils/format.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/cloud_files.dart';
@@ -107,13 +108,23 @@ class AttachmentPreview extends HookConsumerWidget {
static final GlobalKey<SensitiveMarksSelectorState> _sensitiveSelectorKey =
GlobalKey<SensitiveMarksSelectorState>();
Future<void> _showRenameDialog(BuildContext context, WidgetRef ref) async {
final nameController = TextEditingController(text: item.data.name);
String _getDisplayName() {
return item.displayName ??
(item.data is XFile
? (item.data as XFile).name
: item.isOnCloud
? item.data.name
: '');
}
Future<void> _showRenameSheet(BuildContext context, WidgetRef ref) async {
final nameController = TextEditingController(text: _getDisplayName());
String? errorMessage;
await showModalBottomSheet(
context: context,
isScrollControlled: true,
useRootNavigator: true,
builder:
(context) => SheetScaffold(
heightFactor: 0.6,
@@ -152,22 +163,32 @@ class AttachmentPreview extends HookConsumerWidget {
return;
}
try {
showLoadingModal(context);
final apiClient = ref.watch(apiClientProvider);
await apiClient.patch(
'/drive/files/${item.data.id}/name',
data: jsonEncode(newName),
);
final newData = item.data;
newData.name = newName;
final updatedFile = item.copyWith(data: newData);
onUpdate?.call(item.copyWith(data: updatedFile));
if (item.isOnCloud) {
try {
showLoadingModal(context);
final apiClient = ref.watch(apiClientProvider);
await apiClient.patch(
'/drive/files/${item.data.id}/name',
data: jsonEncode(newName),
);
final newData = item.data;
newData.name = newName;
onUpdate?.call(
item.copyWith(
data: newData,
displayName: newName,
),
);
if (context.mounted) Navigator.pop(context);
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
} else {
// Local file rename
onUpdate?.call(item.copyWith(displayName: newName));
if (context.mounted) Navigator.pop(context);
} catch (err) {
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
},
child: Text('rename'.tr()),
@@ -292,6 +313,8 @@ class AttachmentPreview extends HookConsumerWidget {
_ => Symbols.insert_drive_file,
};
final mimeType = FileUploader.getMimeType(item);
if (item.isOnCloud) {
return CloudFileWidget(item: item.data);
} else if (item.data is XFile) {
@@ -321,7 +344,12 @@ class AttachmentPreview extends HookConsumerWidget {
children: [
Icon(fallbackIcon),
const Gap(6),
Text(file.name, textAlign: TextAlign.center),
Text(
_getDisplayName(),
textAlign: TextAlign.center,
),
Text(mimeType, style: TextStyle(fontSize: 10)),
const Gap(1),
FutureBuilder(
future: file.length(),
builder: (context, snapshot) {
@@ -347,6 +375,8 @@ class AttachmentPreview extends HookConsumerWidget {
children: [
Icon(fallbackIcon),
const Gap(6),
Text(mimeType, style: TextStyle(fontSize: 10)),
const Gap(1),
Text(
formatFileSize(item.data.length),
).fontSize(11),
@@ -542,12 +572,20 @@ class AttachmentPreview extends HookConsumerWidget {
onUpdate?.call(item.copyWith(data: result));
},
),
if (item.isOnDevice)
MenuAction(
title: 'rename'.tr(),
image: MenuImage.icon(Symbols.edit),
callback: () async {
await _showRenameSheet(context, ref);
},
),
if (item.isOnCloud)
MenuAction(
title: 'rename'.tr(),
image: MenuImage.icon(Symbols.edit),
callback: () async {
await _showRenameDialog(context, ref);
await _showRenameSheet(context, ref);
},
),
if (item.isOnCloud)

View File

@@ -6,9 +6,8 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/services/file.dart';
import 'package:island/services/file_uploader.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/attachment_preview.dart';
import 'package:material_symbols_icons/symbols.dart';
@@ -42,10 +41,6 @@ class CloudFilePicker extends HookConsumerWidget {
Future<void> startUpload() async {
if (files.value.isEmpty) return;
final baseUrl = ref.read(serverUrlProvider);
final token = await getToken(ref.watch(tokenProvider));
if (token == null) throw Exception("Unauthorized");
List<SnCloudFile> result = List.empty(growable: true);
uploadProgress.value = 0;
@@ -55,19 +50,9 @@ class CloudFilePicker extends HookConsumerWidget {
uploadPosition.value = idx;
final file = files.value[idx];
final cloudFile =
await putFileToCloud(
await FileUploader.createCloudFile(
fileData: file,
atk: token,
baseUrl: baseUrl,
filename: file.data.name ?? 'Post media',
mimetype:
file.data.mimeType ??
switch (file.type) {
UniversalFileType.image => 'image/unknown',
UniversalFileType.video => 'video/unknown',
UniversalFileType.audio => 'audio/unknown',
UniversalFileType.file => 'application/octet-stream',
},
client: ref.read(apiClientProvider),
onProgress: (progress, _) {
uploadProgress.value = progress;
},