💄 Optimize cloud file list

This commit is contained in:
2025-11-02 22:34:32 +08:00
parent 52eff0fa25
commit ab4120cc22
3 changed files with 402 additions and 291 deletions

View File

@@ -0,0 +1,231 @@
import 'dart:io';
import 'dart:math' as math;
import 'package:dismissible_page/dismissible_page.dart';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gal/gal.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/file_info_sheet.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:path/path.dart' show extension;
import 'package:path_provider/path_provider.dart';
import 'package:photo_view/photo_view.dart';
import 'cloud_files.dart';
class CloudFileLightbox extends HookConsumerWidget {
final SnCloudFile item;
final String heroTag;
const CloudFileLightbox({
super.key,
required this.item,
required this.heroTag,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final serverUrl = ref.watch(serverUrlProvider);
final photoViewController = useMemoized(() => PhotoViewController(), []);
final rotation = useState(0);
final showOriginal = useState(false);
Future<void> saveToGallery() async {
try {
// Show loading indicator
showSnackBar('Saving image...');
// Get the image URL
final client = ref.watch(apiClientProvider);
// Create a temporary file to save the image
final tempDir = await getTemporaryDirectory();
var extName = extension(item.name).trim();
if (extName.isEmpty) {
extName = item.mimeType?.split('/').lastOrNull ?? 'jpeg';
}
final filePath = '${tempDir.path}/${item.id}.$extName';
await client.download(
'/drive/files/${item.id}',
filePath,
queryParameters: {'original': true},
);
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
// Save to gallery
await Gal.putImage(filePath, album: 'Solar Network');
// Show success message
showSnackBar('Image saved to gallery');
} else {
await FileSaver.instance.saveFile(
name: item.name.isEmpty ? '${item.id}.$extName' : item.name,
file: File(filePath),
);
showSnackBar('Image saved to $filePath');
}
} catch (e) {
showErrorAlert(e);
}
}
void showInfoSheet() {
showModalBottomSheet(
useRootNavigator: true,
context: context,
isScrollControlled: true,
builder: (context) => FileInfoSheet(item: item),
);
}
final shadow = [
Shadow(color: Colors.black54, blurRadius: 5.0, offset: Offset(1.0, 1.0)),
];
return DismissiblePage(
isFullScreen: true,
backgroundColor: Colors.transparent,
direction: DismissiblePageDismissDirection.down,
onDismissed: () {
Navigator.of(context).pop();
},
child: Stack(
children: [
Positioned.fill(
child: PhotoView(
backgroundDecoration: BoxDecoration(
color: Colors.black.withOpacity(0.9),
),
controller: photoViewController,
heroAttributes: PhotoViewHeroAttributes(tag: heroTag),
imageProvider: CloudImageWidget.provider(
fileId: item.id,
serverUrl: serverUrl,
original: showOriginal.value,
),
// Apply rotation transformation
customSize: MediaQuery.of(context).size,
basePosition: Alignment.center,
filterQuality: FilterQuality.high,
),
),
// Close button and save button
Positioned(
top: MediaQuery.of(context).padding.top + 16,
right: 16,
left: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
if (!kIsWeb)
IconButton(
icon: Icon(
Icons.save_alt,
color: Colors.white,
shadows: shadow,
),
onPressed: () async {
saveToGallery();
},
),
IconButton(
onPressed: () {
showOriginal.value = !showOriginal.value;
},
icon: Icon(
showOriginal.value ? Symbols.hd : Symbols.sd,
color: Colors.white,
shadows: shadow,
),
),
],
),
IconButton(
icon: Icon(Icons.close, color: Colors.white, shadows: shadow),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
// Rotation controls
Positioned(
bottom: MediaQuery.of(context).padding.bottom + 16,
left: 16,
right: 16,
child: Row(
children: [
IconButton(
icon: Icon(
Icons.info_outline,
color: Colors.white,
shadows: shadow,
),
onPressed: showInfoSheet,
),
Spacer(),
IconButton(
icon: Icon(
Icons.remove,
color: Colors.white,
shadows: shadow,
),
onPressed: () {
photoViewController.scale =
(photoViewController.scale ?? 1) - 0.05;
},
),
IconButton(
icon: Icon(Icons.add, color: Colors.white, shadows: shadow),
onPressed: () {
photoViewController.scale =
(photoViewController.scale ?? 1) + 0.05;
},
),
const Gap(8),
IconButton(
icon: Icon(
Icons.rotate_left,
color: Colors.white,
shadows: shadow,
),
onPressed: () {
rotation.value = (rotation.value - 1) % 4;
photoViewController.rotation =
rotation.value * -math.pi / 2;
},
),
IconButton(
icon: Icon(
Icons.rotate_right,
color: Colors.white,
shadows: [
Shadow(
color: Colors.black54,
blurRadius: 5.0,
offset: Offset(1.0, 1.0),
),
],
),
onPressed: () {
rotation.value = (rotation.value + 1) % 4;
photoViewController.rotation =
rotation.value * -math.pi / 2;
},
),
],
),
),
],
),
);
}
}