396 lines
11 KiB
Dart
396 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'dart:convert';
|
|
import 'main.dart';
|
|
import 'sidebar.dart';
|
|
import 'shares.dart';
|
|
import 'sharedialog.dart';
|
|
import 'package:path_provider/path_provider.dart';
|
|
import 'package:file_saver/file_saver.dart';
|
|
import 'dart:io';
|
|
import 'config.dart';
|
|
|
|
class FileInfo {
|
|
final int id;
|
|
final String name;
|
|
final int size;
|
|
final DateTime uploadedAt;
|
|
|
|
FileInfo({
|
|
required this.id,
|
|
required this.name,
|
|
required this.size,
|
|
required this.uploadedAt,
|
|
});
|
|
|
|
factory FileInfo.fromJson(Map<String, dynamic> json) {
|
|
return FileInfo(
|
|
id: json['id'],
|
|
name: json['original_name'],
|
|
size: json['size'],
|
|
uploadedAt: DateTime.parse(json['uploaded_at']),
|
|
);
|
|
}
|
|
}
|
|
|
|
class MainPage extends StatefulWidget {
|
|
const MainPage({super.key});
|
|
|
|
@override
|
|
State<MainPage> createState() => _MainPageState();
|
|
}
|
|
|
|
class _MainPageState extends State<MainPage> {
|
|
SidebarPage _selectedPage = SidebarPage.myFiles;
|
|
List<FileInfo> _files = [];
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_fetchFiles();
|
|
}
|
|
|
|
Future<void> _fetchFiles() 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('${Config.apiUrl}/files');
|
|
final response = await http.get(
|
|
uri,
|
|
headers: {'Authorization': 'Bearer $jwt'},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final List<dynamic> data = jsonDecode(response.body);
|
|
setState(() {
|
|
_files = data.map((file) => FileInfo.fromJson(file)).toList();
|
|
_isLoading = false;
|
|
});
|
|
} else {
|
|
setState(() {
|
|
_error = 'Errore durante il recupero dei file.';
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
_error = 'Errore di rete: ${e.toString()}';
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _logout() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.remove('jwt');
|
|
if (mounted) {
|
|
Navigator.of(context).pushAndRemoveUntil(
|
|
MaterialPageRoute(builder: (_) => const AuthScreen()),
|
|
(route) => false,
|
|
);
|
|
}
|
|
}
|
|
|
|
void _onPageSelected(SidebarPage page) {
|
|
setState(() {
|
|
_selectedPage = page;
|
|
});
|
|
}
|
|
|
|
Future<void> _uploadFile() async {
|
|
final result = await FilePicker.platform.pickFiles();
|
|
if (result == null || result.files.isEmpty) return;
|
|
|
|
final file = result.files.first;
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final jwt = prefs.getString('jwt');
|
|
|
|
if (jwt == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Sessione scaduta, effettua di nuovo il login.')),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final uri = Uri.parse('${Config.apiUrl}/upload');
|
|
final request = http.MultipartRequest('POST', uri)
|
|
..headers['Authorization'] = 'Bearer $jwt'
|
|
..files.add(
|
|
http.MultipartFile.fromBytes(
|
|
'file',
|
|
file.bytes!,
|
|
filename: file.name,
|
|
),
|
|
);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Caricamento di ${file.name} in corso...')),
|
|
);
|
|
final response = await request.send();
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('File caricato con successo!')),
|
|
);
|
|
_fetchFiles(); // Refresh the file list
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Errore durante l\'upload.')),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _downloadFile(FileInfo file) async {
|
|
try {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final jwt = prefs.getString('jwt');
|
|
|
|
if (jwt == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Sessione scaduta, effettua di nuovo il login.')),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final uri = Uri.parse('${Config.apiUrl}/files/${file.id}');
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Download in corso...')),
|
|
);
|
|
|
|
final response = await http.get(
|
|
uri,
|
|
headers: {'Authorization': 'Bearer $jwt'},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
if (Platform.isAndroid || Platform.isIOS) {
|
|
await FileSaver.instance.saveFile(
|
|
name: file.name,
|
|
bytes: response.bodyBytes,
|
|
);
|
|
} else {
|
|
// Desktop platforms
|
|
final directory = await getDownloadsDirectory();
|
|
if (directory != null) {
|
|
final filePath = '${directory.path}/${file.name}';
|
|
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 ${file.name} 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()}')),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _deleteFile(FileInfo file) async {
|
|
try {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final jwt = prefs.getString('jwt');
|
|
|
|
if (jwt == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Sessione scaduta, effettua di nuovo il login.')),
|
|
);
|
|
return;
|
|
}
|
|
|
|
final uri = Uri.parse('${Config.apiUrl}/files/${file.id}');
|
|
final response = await http.delete(
|
|
uri,
|
|
headers: {'Authorization': 'Bearer $jwt'},
|
|
);
|
|
|
|
if (response.statusCode == 204) {
|
|
setState(() {
|
|
_files.removeWhere((f) => f.id == file.id);
|
|
});
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('File ${file.name} eliminato con successo!')),
|
|
);
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(content: Text('Errore durante l\'eliminazione.')),
|
|
);
|
|
}
|
|
} catch (e) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Errore di rete: ${e.toString()}')),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _showShareDialog(FileInfo file) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => ShareDialog(
|
|
fileId: file.id,
|
|
fileName: file.name,
|
|
),
|
|
);
|
|
}
|
|
|
|
void _showFileOptions(FileInfo file) {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
builder: (context) => SafeArea(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
ListTile(
|
|
leading: const Icon(Icons.download),
|
|
title: const Text('Download'),
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
_downloadFile(file);
|
|
},
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.share),
|
|
title: const Text('Condividi'),
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
_showShareDialog(file);
|
|
},
|
|
),
|
|
ListTile(
|
|
leading: Icon(Icons.delete, color: Colors.red[700]),
|
|
title: Text('Elimina', style: TextStyle(color: Colors.red[700])),
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
_deleteFile(file);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMyFiles() {
|
|
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: _fetchFiles,
|
|
child: const Text('Riprova'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
if (_files.isEmpty) {
|
|
return const Center(
|
|
child: Text('Nessun file trovato. Carica il tuo primo file!'),
|
|
);
|
|
}
|
|
|
|
return ListView.separated(
|
|
padding: const EdgeInsets.all(32),
|
|
itemCount: _files.length,
|
|
separatorBuilder: (_, __) => const Divider(),
|
|
itemBuilder: (context, idx) {
|
|
final file = _files[idx];
|
|
return ListTile(
|
|
leading: const Icon(Icons.insert_drive_file),
|
|
title: Text(file.name),
|
|
subtitle: Text(
|
|
"Size: ${(file.size / 1024).toStringAsFixed(1)} KB • Uploaded: ${file.uploadedAt.toLocal()}",
|
|
style: const TextStyle(fontSize: 12),
|
|
),
|
|
trailing: IconButton(
|
|
icon: const Icon(Icons.more_vert),
|
|
onPressed: () => _showFileOptions(file),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
Widget content;
|
|
if (_selectedPage == SidebarPage.myFiles) {
|
|
content = Stack(
|
|
children: [
|
|
_buildMyFiles(),
|
|
Positioned(
|
|
bottom: 32,
|
|
right: 32,
|
|
child: FloatingActionButton.extended(
|
|
onPressed: _uploadFile,
|
|
icon: const Icon(Icons.upload_file),
|
|
label: const Text("Upload"),
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
} else if (_selectedPage == SidebarPage.shared) {
|
|
content = const SharesPage();
|
|
} else {
|
|
content = const Center(child: Text("Settings (coming soon)"));
|
|
}
|
|
|
|
return Scaffold(
|
|
body: Row(
|
|
children: [
|
|
Sidebar(
|
|
selectedPage: _selectedPage,
|
|
onPageSelected: _onPageSelected,
|
|
onLogout: _logout,
|
|
),
|
|
Expanded(child: content),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|