Enhance file download functionality with snackbar notifications and authentication token handling
This commit is contained in:
@@ -10,6 +10,8 @@ import '../blocs/file_browser/file_browser_state.dart';
|
|||||||
import '../blocs/permission/permission_bloc.dart';
|
import '../blocs/permission/permission_bloc.dart';
|
||||||
import '../blocs/permission/permission_event.dart';
|
import '../blocs/permission/permission_event.dart';
|
||||||
import '../blocs/permission/permission_state.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_bloc.dart';
|
||||||
import '../blocs/upload/upload_event.dart';
|
import '../blocs/upload/upload_event.dart';
|
||||||
import '../blocs/upload/upload_state.dart';
|
import '../blocs/upload/upload_state.dart';
|
||||||
@@ -282,15 +284,59 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _downloadFile(FileItem file) async {
|
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 {
|
try {
|
||||||
final fileService = getIt<FileService>();
|
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(
|
final downloadUrl = await fileService.getDownloadUrl(
|
||||||
widget.orgId,
|
widget.orgId,
|
||||||
file.path,
|
file.path,
|
||||||
);
|
);
|
||||||
|
|
||||||
// For web, use the download URL with the ApiClient base URL (from DI)
|
// Build full URL with token for authentication (anchor elements can't send headers)
|
||||||
final fullUrl = '${fileService.baseUrl}$downloadUrl';
|
final fullUrl = '${fileService.baseUrl}$downloadUrl&token=${Uri.encodeComponent(token)}';
|
||||||
|
|
||||||
// Trigger download via anchor element
|
// Trigger download via anchor element
|
||||||
final anchor = web.HTMLAnchorElement()
|
final anchor = web.HTMLAnchorElement()
|
||||||
@@ -298,16 +344,50 @@ class _FileExplorerState extends State<FileExplorer> {
|
|||||||
..download = file.name;
|
..download = file.name;
|
||||||
anchor.click();
|
anchor.click();
|
||||||
|
|
||||||
|
// Dismiss preparing snackbar and show success
|
||||||
|
snackController?.close();
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
context,
|
SnackBar(
|
||||||
).showSnackBar(SnackBar(content: Text('Downloading ${file.name}')));
|
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) {
|
} catch (e) {
|
||||||
|
snackController?.close();
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
context,
|
SnackBar(
|
||||||
).showSnackBar(SnackBar(content: Text('Download failed: $e')));
|
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,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user