Enhance file download functionality with snackbar notifications and authentication token handling

This commit is contained in:
Leon Bösche
2026-01-13 22:41:56 +01:00
parent 847a8de414
commit 03962d5a80

View File

@@ -10,6 +10,8 @@ import '../blocs/file_browser/file_browser_state.dart';
import '../blocs/permission/permission_bloc.dart';
import '../blocs/permission/permission_event.dart';
import '../blocs/permission/permission_state.dart';
import '../blocs/session/session_bloc.dart';
import '../blocs/session/session_state.dart';
import '../blocs/upload/upload_bloc.dart';
import '../blocs/upload/upload_event.dart';
import '../blocs/upload/upload_state.dart';
@@ -282,15 +284,59 @@ class _FileExplorerState extends State<FileExplorer> {
}
void _downloadFile(FileItem file) async {
// Show download starting snackbar
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? snackController;
if (context.mounted) {
snackController = ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: AppTheme.accentColor,
backgroundColor: AppTheme.accentColor.withValues(alpha: 0.3),
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Preparing download: ${file.name}',
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: AppTheme.primaryText),
),
),
],
),
duration: const Duration(seconds: 3),
backgroundColor: AppTheme.primaryBackground,
),
);
}
try {
final fileService = getIt<FileService>();
// Get token from SessionBloc for authentication
final sessionState = context.read<SessionBloc>().state;
String? token;
if (sessionState is SessionActive) {
token = sessionState.token;
}
if (token == null) {
throw Exception('Not authenticated');
}
final downloadUrl = await fileService.getDownloadUrl(
widget.orgId,
file.path,
);
// For web, use the download URL with the ApiClient base URL (from DI)
final fullUrl = '${fileService.baseUrl}$downloadUrl';
// Build full URL with token for authentication (anchor elements can't send headers)
final fullUrl = '${fileService.baseUrl}$downloadUrl&token=${Uri.encodeComponent(token)}';
// Trigger download via anchor element
final anchor = web.HTMLAnchorElement()
@@ -298,16 +344,50 @@ class _FileExplorerState extends State<FileExplorer> {
..download = file.name;
anchor.click();
// Dismiss preparing snackbar and show success
snackController?.close();
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Downloading ${file.name}')));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.download_done, color: AppTheme.accentColor, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
'Downloaded ${file.name}',
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: AppTheme.primaryText),
),
),
],
),
duration: const Duration(seconds: 2),
backgroundColor: AppTheme.primaryBackground,
),
);
}
} catch (e) {
snackController?.close();
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Download failed: $e')));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.error_outline, color: Colors.red, size: 20),
const SizedBox(width: 12),
Expanded(
child: Text(
'Download failed: $e',
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: AppTheme.primaryText),
),
),
],
),
backgroundColor: AppTheme.primaryBackground,
),
);
}
}
}