import 'dart:io'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:path/path.dart' as path; import 'package:spotube/extensions/context.dart'; import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/sourced_track/enums.dart'; final codecs = SourceCodecs.values.map((s) => s.name); class LocalFolderCacheExportDialog extends HookConsumerWidget { final Directory exportDir; final Directory cacheDir; const LocalFolderCacheExportDialog({ super.key, required this.exportDir, required this.cacheDir, }); @override Widget build(BuildContext context, ref) { final ThemeData(:typography, :colorScheme) = Theme.of(context); final files = useState>([]); final filesExported = useState(0); useEffect(() { final stream = cacheDir.list().where( (event) => event is File && codecs.contains(path.extension(event.path).replaceAll(".", "")), ); stream.listen( (event) { files.value = [...files.value, event as File]; }, onError: (e, stack) { AppLogger.reportError(e, stack); }, ); return null; }, []); useEffect(() { if (filesExported.value == files.value.length && filesExported.value > 0) { Navigator.of(context).pop(); } return null; }, [filesExported.value, files.value]); final isExportInProgress = filesExported.value > 0 && filesExported.value != files.value.length; return AlertDialog( title: Text(context.l10n.export_cache_files), content: AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: filesExported.value == 0 ? Column( mainAxisSize: MainAxisSize.min, children: [ Text( context.l10n.found_n_files(files.value.length.toString()), ), const Gap(10), Text.rich( TextSpan( children: [ TextSpan( text: context.l10n.export_cache_confirmation, ), TextSpan( text: "\n${exportDir.path}?", style: typography.small.copyWith( color: colorScheme.mutedForeground, ), ), ], ), ), ], ) : Column( mainAxisSize: MainAxisSize.min, children: [ Text( context.l10n.exported_n_out_of_m_files( files.value.length.toString(), filesExported.value.toString(), ), ), const Gap(10), LinearProgressIndicator( value: filesExported.value / files.value.length, ), ], ), ), actions: [ Button.outline( onPressed: isExportInProgress ? null : () { Navigator.of(context).pop(); }, child: Text(context.l10n.cancel), ), Button.primary( onPressed: isExportInProgress ? null : () async { for (final file in files.value) { try { final destinationFile = File( path.join(exportDir.path, path.basename(file.path)), ); if (await destinationFile.exists()) { await destinationFile.delete(); } await file.copy(destinationFile.path); filesExported.value++; } catch (e, stack) { AppLogger.reportError(e, stack); continue; } } }, child: Text(context.l10n.export), ), ], ); } }