litecloud/lightcloud_app/lib/shares.dart
Mercurio a95d455b2e feat: implement file sharing and management functionality
- Add config.dart for API configuration
- Implement sidebar navigation with logout functionality
- Add file sharing dialog with public/private options
- Create shares page to view and manage shared files
- Implement file upload/download/delete operations
- Add authentication persistence using shared preferences
- Configure CORS for API to enable cross-origin requests
2025-06-02 15:37:06 +02:00

217 lines
6.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:file_saver/file_saver.dart';
import 'dart:io';
class SharedFile {
final String id;
final int fileId;
final String fileName;
final DateTime createdAt;
final DateTime? expiresAt;
SharedFile({
required this.id,
required this.fileId,
required this.fileName,
required this.createdAt,
this.expiresAt,
});
}
class SharesPage extends StatefulWidget {
const SharesPage({super.key});
@override
State<SharesPage> createState() => _SharesPageState();
}
class _SharesPageState extends State<SharesPage> {
List<SharedFile> _sharedFiles = [];
bool _isLoading = false;
String? _error;
@override
void initState() {
super.initState();
_fetchSharedFiles();
}
Future<void> _fetchSharedFiles() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
final prefs = await SharedPreferences.getInstance();
final jwt = prefs.getString('jwt');
if (jwt == null) {
setState(() {
_error = 'Sessione scaduta, effettua di nuovo il login.';
_isLoading = false;
});
return;
}
final uri = Uri.parse('http://localhost:8082/api/shares');
final response = await http.get(
uri,
headers: {'Authorization': 'Bearer $jwt'},
);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
setState(() {
_sharedFiles = data
.map((share) => SharedFile(
id: share['id'],
fileId: share['file_id'],
fileName: share['file_name'],
createdAt: DateTime.parse(share['created_at']),
expiresAt: share['expires_at'] != null
? DateTime.parse(share['expires_at'])
: null,
))
.toList();
_isLoading = false;
});
} else {
setState(() {
_error = 'Errore durante il recupero dei file condivisi.';
_isLoading = false;
});
}
} catch (e) {
setState(() {
_error = 'Errore di rete: ${e.toString()}';
_isLoading = false;
});
}
}
Future<void> _downloadSharedFile(SharedFile share) async {
try {
final shareLink = 'http://localhost:8082/api/shared/${share.id}';
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Download in corso...')),
);
final response = await http.get(Uri.parse(shareLink));
if (response.statusCode == 200) {
// Save the file to disk
if (Platform.isAndroid || Platform.isIOS) {
// Mobile platforms
await FileSaver.instance.saveFile(
name: share.fileName,
bytes: response.bodyBytes,
);
} else {
// Desktop platforms
final directory = await getDownloadsDirectory();
if (directory != null) {
final filePath = '${directory.path}/${share.fileName}';
final fileObj = File(filePath);
await fileObj.writeAsBytes(response.bodyBytes);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('File salvato in: $filePath')),
);
} else {
throw Exception('Could not access downloads directory');
}
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('File ${share.fileName} scaricato con successo!')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Errore durante il download.')),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Errore di rete: ${e.toString()}')),
);
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (_error != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_error!, style: TextStyle(color: Colors.red[700])),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _fetchSharedFiles,
child: const Text('Riprova'),
),
],
),
);
}
if (_sharedFiles.isEmpty) {
return const Center(
child: Text(
'Nessun file condiviso. Condividi un file dalla sezione "My Files"!'),
);
}
return ListView.separated(
padding: const EdgeInsets.all(32),
itemCount: _sharedFiles.length,
separatorBuilder: (_, __) => const Divider(),
itemBuilder: (context, idx) {
final share = _sharedFiles[idx];
return ListTile(
leading: const Icon(Icons.link),
title: Text(share.fileName),
subtitle: Text(
"Creato: ${share.createdAt.toLocal()}${share.expiresAt != null ? ' • Scade: ${share.expiresAt!.toLocal()}' : ' • Non scade mai'}",
style: const TextStyle(fontSize: 12),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.download),
tooltip: 'Scarica file',
onPressed: () => _downloadSharedFile(share),
),
IconButton(
icon: const Icon(Icons.copy),
tooltip: 'Copia link',
onPressed: () {
final shareLink =
'http://localhost:8082/api/shared/${share.id}';
Clipboard.setData(ClipboardData(text: shareLink));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Link copiato negli appunti')),
);
},
),
],
),
);
},
);
}
}